upstream version 1.2.2
[debian/freetts] / com / sun / speech / freetts / en / us / FeatureProcessors.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.en.us;
12
13 import java.util.HashSet;
14 import java.util.Set;
15 import java.util.regex.Pattern;
16
17 import com.sun.speech.freetts.FeatureProcessor;
18 import com.sun.speech.freetts.Item;
19 import com.sun.speech.freetts.PartOfSpeech;
20 import com.sun.speech.freetts.PathExtractor;
21 import com.sun.speech.freetts.PathExtractorImpl;
22 import com.sun.speech.freetts.ProcessException;
23 import com.sun.speech.freetts.Relation;
24 import com.sun.speech.freetts.Voice;
25
26
27
28 /**
29  * Provides the set of feature processors that are used by this
30  * language as part of the CART processing.
31  */
32 public class FeatureProcessors {
33
34     private final static PathExtractor FIRST_SYLLABLE_PATH = 
35         new PathExtractorImpl(
36       "R:SylStructure.parent.R:Phrase.parent.daughter.R:SylStructure.daughter",
37        false);
38
39     private final static PathExtractor LAST_SYLLABLE_PATH = 
40         new PathExtractorImpl(
41       "R:SylStructure.parent.R:Phrase.parent.daughtern.R:SylStructure.daughter",
42       false);
43
44     private final static PathExtractor LAST_LAST_SYLLABLE_PATH = 
45         new PathExtractorImpl(
46   "R:SylStructure.parent.R:Phrase.parent.daughtern.R:SylStructure.daughtern",
47       false);
48
49     private final static PathExtractor SUB_PHRASE_PATH = 
50         new PathExtractorImpl("R:SylStructure.parent.R:Phrase.parent.p", false);
51
52     private final static Pattern DOUBLE_PATTERN 
53         = Pattern.compile(USEnglish.RX_DOUBLE);
54
55     private final static Pattern DIGITS_PATTERN  
56         = Pattern.compile(USEnglish.RX_DIGITS);
57
58     private static Set months;
59     private static Set days;
60
61     // the set of month names
62     static {
63         months = new HashSet();
64         months.add("jan");
65         months.add("january");
66         months.add("feb");
67         months.add("february");
68         months.add("mar");
69         months.add("march");
70         months.add("apr");
71         months.add("april");
72         months.add("may");
73         months.add("jun");
74         months.add("june");
75         months.add("jul");
76         months.add("july");
77         months.add("aug");
78         months.add("august");
79         months.add("sep");
80         months.add("september");
81         months.add("oct");
82         months.add("october");
83         months.add("nov");
84         months.add("november");
85         months.add("dec");
86         months.add("december");
87     }
88
89     // the set of week neames
90     static {
91         days = new HashSet();
92         days.add("sun");
93         days.add("sunday");
94         days.add("mon");
95         days.add("monday");
96         days.add("tue");
97         days.add("tuesday");
98         days.add("wed");
99         days.add("wednesday");
100         days.add("thu");
101         days.add("thursday");
102         days.add("fri");
103         days.add("friday");
104         days.add("sat");
105         days.add("saturday");
106     }
107
108     // no instances
109     private FeatureProcessors() {}
110
111     /**
112      * Returns a guess of the part-of-speech.
113      *
114      * This is a feature processor. A feature processor takes an item,
115      * performs some sort of processing on the item and returns an object.
116      */
117     public static class Gpos implements FeatureProcessor {
118         PartOfSpeech pos;
119         /**
120          * Creates a GPOS with the given part-of-speech table
121          *
122          * @param pos part of speech determiner
123          */
124         public Gpos(PartOfSpeech pos) {
125             this.pos = pos;
126         }
127
128         /**
129          * Performs some processing on the given item.
130          *
131          * @param  item  the item to process
132          *
133          * @return a guess at the part-of-speech for the item
134          *
135          * @throws ProcessException if an exception occurred during the
136          * processing
137          */
138         public String process(Item item) throws ProcessException {
139             return pos.getPartOfSpeech(item.toString());
140         }
141     }
142
143
144     /**
145      * Returns as an Integer the number of syllables in the given
146      * word.  This is a feature processor. A feature processor takes an item,
147      * performs some sort of processing on the item and returns an object.
148      */
149     public static class WordNumSyls implements FeatureProcessor {
150
151         /**
152          * Performs some processing on the given item.
153          *
154          * @param  item  the item to process
155          *
156          * @return the number of syllables in the given word
157          *
158          * @throws ProcessException if an exception occurred during the
159          * processing
160          */
161         public String process(Item item) throws ProcessException {
162             int count = 0;
163             Item daughter = item.getItemAs(
164                 Relation.SYLLABLE_STRUCTURE).getDaughter();
165             while (daughter != null) {
166                 count++;
167                 daughter = daughter.getNext();
168             }
169             return Integer.toString(rail(count));
170         }
171     }
172
173     /**
174      * Counts the number of accented syllables since the last major break.
175      * This is a feature processor. A feature processor takes an item,
176      * performs some sort of processing on the item and returns an object.
177      */
178     public static class AccentedSylIn implements FeatureProcessor {
179
180         /**
181          * Performs some processing on the given item.
182          *
183          * @param  item  the item to process
184          *
185          * @return the number of accented syllables since the last
186          *    major break
187          *
188          * @throws ProcessException if an exception occurred during the
189          * processing
190          */
191         public String process(Item item) throws ProcessException {
192             int count = 0;
193             Item ss = item.getItemAs(Relation.SYLLABLE);
194             Item firstSyllable  = FIRST_SYLLABLE_PATH.findItem(item);
195
196             for (Item p = ss; p != null; p = p.getPrevious() )  {
197                 if (isAccented(p)) {
198                     count++;
199                 }
200                 if (p.equalsShared(firstSyllable)) {
201                     break;
202                 }
203             }
204             return Integer.toString(rail(count));
205         }
206     }
207
208
209     /**
210      * Counts the number of stressed syllables since the last major break.
211      * This is a feature processor. A feature processor takes an item,
212      * performs some sort of processing on the item and returns an object.
213      */
214     public static class StressedSylIn implements FeatureProcessor {
215
216         /**
217          * Performs some processing on the given item.
218          *
219          * @param  item  the item to process
220          *
221          * @return the number of stresses syllables since the last
222          * major break
223          *
224          * @throws ProcessException if an exception occurred during the
225          * processing
226          */
227         public String process(Item item) throws ProcessException {
228             int count = 0;
229             Item ss = item.getItemAs(Relation.SYLLABLE);
230             Item firstSyllable  = FIRST_SYLLABLE_PATH.findItem(item);
231
232             // this should include the first syllable, but
233             // flite 1.1 and festival don't.
234
235             for (Item p = ss.getPrevious(); 
236                     p != null && !p.equalsShared(firstSyllable);
237                     p = p.getPrevious() )  {
238                 if ("1".equals(p.getFeatures().getString("stress"))) {
239                     count++;
240                 }
241             }
242             return Integer.toString(rail(count));
243         }
244     }
245
246     /**
247      * Counts the number of stressed syllables until the next major break.
248      * This is a feature processor. A feature processor takes an item,
249      * performs some sort of processing on the item and returns an object.
250      */
251     public static class StressedSylOut implements FeatureProcessor {
252
253         /**
254          * Performs some processing on the given item.
255          *
256          * @param  item  the item to process
257          *
258          * @return the number of stressed syllables until the next major break
259          *
260          * @throws ProcessException if an exception occurred during the
261          * processing
262          */
263         public String process(Item item) throws ProcessException {
264             int count = 0;
265             Item ss = item.getItemAs(Relation.SYLLABLE);
266             Item lastSyllable  = LAST_SYLLABLE_PATH.findItem(item);
267
268             for (Item p = ss.getNext(); p != null; p = p.getNext() )  {
269                 if ("1".equals(p.getFeatures().getString("stress"))) {
270                     count++;
271                 }
272                 if (p.equalsShared(lastSyllable)) {
273                     break;
274                 }
275             }
276             return Integer.toString(rail(count));
277         }
278     }
279
280     /**
281      * Returns the length of the string. (generally this is a digit
282      * string)
283      * This is a feature processor. A feature processor takes an item,
284      * performs some sort of processing on the item and returns an object.
285      */
286     public static class NumDigits implements FeatureProcessor {
287
288         /**
289          * Performs some processing on the given item.
290          *
291          * @param  item  the item to process
292          *
293          * @return the length of the string
294          *
295          * @throws ProcessException if an exception occurred during the
296          * processing
297          */
298         public String process(Item item) throws ProcessException {
299             String name = item.getFeatures().getString("name");
300             return Integer.toString(rail(name.length()));
301         }
302     }
303
304     /**
305      * Returns true ("1") if the given item is a number between 0 and
306      * 32 exclusive, otherwise, returns "0".
307      * string)
308      * This is a feature processor. A feature processor takes an item,
309      * performs some sort of processing on the item and returns an object.
310      */
311     public static class MonthRange implements FeatureProcessor {
312
313         /**
314          * Performs some processing on the given item.
315          *
316          * @param  item  the item to process
317          *
318          * @return returns "1" if the given item is a number between 0
319          * and 32 (exclusive) otherwise returns "0"
320          *
321          * @throws ProcessException if an exception occurred during the
322          * processing
323          */
324         public String process(Item item) throws ProcessException {
325             int v = Integer.parseInt(item.getFeatures().getString("name"));
326             if ((v > 0) && (v < 32)) {
327                 return "1";
328             } else {
329                 return "0";
330             }
331         }
332     }
333
334     /**
335      * Attempts to guess the part of speech.
336      * This is a feature processor. A feature processor takes an item,
337      * performs some sort of processing on the item and returns an object.
338      */
339     public static class TokenPosGuess implements FeatureProcessor {
340         /**
341          * Performs some processing on the given item.
342          *
343          * @param  item  the item to process
344          *
345          * @return  a guess at the part of speech
346          *
347          * @throws ProcessException if an exception occurred during the
348          * processing
349          */
350         public String process(Item item) throws ProcessException {
351             String name = item.getFeatures().getString("name");
352             String dc = name.toLowerCase();
353             if (DIGITS_PATTERN.matcher(dc).matches()) {
354                 return "numeric";
355             } else if (DOUBLE_PATTERN.matcher(dc).matches()) {
356                 return "number";
357             } else if (months.contains(dc)) {
358                 return "month";
359             } else if (days.contains(dc)) {
360                 return "day";
361             } else if (dc.equals("a")) {
362                 return "a";
363             } else if (dc.equals("flight")) {
364                 return "flight";
365             } else if (dc.equals("to")) {
366                 return "to";
367             } else {
368                 return "_other_";
369             }
370         }
371     }
372
373     /**
374      * Checks to see if the given syllable is accented.
375      * This is a feature processor. A feature processor takes an item,
376      * performs some sort of processing on the item and returns an object.
377      */
378     public static class Accented implements FeatureProcessor {
379
380         /**
381          * Performs some processing on the given item.
382          * @param  item  the item to process
383          *
384          * @return "1" if the syllable is accented; otherwise "0"
385          *
386          * @throws ProcessException if an exception occurred during the
387          * processing
388          */
389         public String process(Item item) throws ProcessException {
390             if (isAccented(item)) {
391                 return "1";
392             } else {
393                 return "0";
394             }
395         }
396     }
397
398     /**
399      * Find the last accented syllable
400      * This is a feature processor. A feature processor takes an item,
401      * performs some sort of processing on the item and returns an object.
402      */
403     public static class LastAccent implements FeatureProcessor {
404
405         /**
406          * Performs some processing on the given item.
407          *
408          * @param  item  the item to process
409          * 
410          * @return the count of the last accented syllable
411          *
412          * @throws ProcessException if an exception occurred during the
413          * processing
414          */
415         public String process(Item item) throws ProcessException {
416             int count = 0;
417
418             for (Item p = item.getItemAs(Relation.SYLLABLE); 
419                       p != null; p = p.getPrevious(), count++)  {
420                 if (isAccented(p)) {
421                     break;
422                 }
423             }
424             return Integer.toString(rail(count));
425         }
426     }
427
428     /**
429      * Finds the position of the phoneme in the syllable
430      * This is a feature processor. A feature processor takes an item,
431      * performs some sort of processing on the item and returns an object.
432      */
433     public static class PosInSyl implements FeatureProcessor {
434
435         /**
436          * Performs some processing on the given item.
437          *
438          * @param  item  the item to process
439          *
440          * @return the position of the phoneme in the syllable
441          *
442          * @throws ProcessException if an exception occurred during the
443          * processing
444          */
445         public String process(Item item) throws ProcessException {
446             int count = -1;
447
448             for (Item p = item.getItemAs(Relation.SYLLABLE_STRUCTURE); 
449                       p != null; p = p.getPrevious() )  {
450                 count++;
451             }
452             return Integer.toString(rail(count));
453         }
454     }
455
456     /**
457      * Classifies the the syllable as single, initial, mid or final.
458      * This is a feature processor. A feature processor takes an item,
459      * performs some sort of processing on the item and returns an object.
460      */
461     public static class PositionType implements FeatureProcessor {
462
463         /**
464          * Performs some processing on the given item.
465          *
466          * @param  item  the item to process
467          *
468          * @return classifies the syllable as "single", "final",
469          * "initial" or "mid"
470          *
471          * @throws ProcessException if an exception occurred during the
472          * processing
473          */
474         public String process(Item item) throws ProcessException {
475             String type;
476
477             Item s = item.getItemAs(Relation.SYLLABLE_STRUCTURE); 
478             if (s == null) {
479                 type = "single";
480             } else if (s.getNext() == null) {
481                 if (s.getPrevious() == null) {
482                     type = "single";
483                 } else {
484                     type = "final";
485                 }
486             } else if (s.getPrevious() == null) {
487                 type = "initial";
488             } else {
489                 type = "mid";
490             }
491             return type;
492         }
493     }
494
495
496     /**
497      * Counts the number of stressed syllables since the last major break.
498      * This is a feature processor. A feature processor takes an item,
499      * performs some sort of processing on the item and returns an object.
500      */
501     public static class SylIn implements FeatureProcessor {
502
503         /**
504          * Performs some processing on the given item.
505          *
506          * @param  item  the item to process
507          * 
508          * @return the number of stressed syllables since the last
509          * major break
510          *
511          * @throws ProcessException if an exception occurred during the
512          * processing
513          */
514         public String process(Item item) throws ProcessException {
515             int count = 0;
516             Item ss = item.getItemAs(Relation.SYLLABLE);
517             Item firstSyllable  = FIRST_SYLLABLE_PATH.findItem(item);
518
519             for (Item p = ss; p != null; p = p.getPrevious(), count++ )  {
520                 if (p.equalsShared(firstSyllable)) {
521                     break;
522                 }
523             }
524             return Integer.toString(rail(count));
525         }
526     }
527
528     /**
529      * Counts the number of stressed syllables since the last major break.
530      * This is a feature processor. A feature processor takes an item,
531      * performs some sort of processing on the item and returns an object.
532      */
533     public static class SylOut implements FeatureProcessor {
534
535         /**
536          * Performs some processing on the given item.
537          *
538          * @param  item  the item to process
539          *
540          * @return the number of stressed syllables since the last
541          * major break
542          *
543          * @throws ProcessException if an exception occurred during the
544          * processing
545          */
546         public String process(Item item) throws ProcessException {
547             int count = 0;
548             Item ss = item.getItemAs(Relation.SYLLABLE);
549             Item firstSyllable  = LAST_LAST_SYLLABLE_PATH.findItem(item);
550
551             for (Item p = ss; p != null; p = p.getNext() )  {
552                 if (p.equalsShared(firstSyllable)) {
553                     break;
554                 }
555                 count++;
556             }
557             return Integer.toString(rail(count));
558         }
559     }
560
561
562     /**
563      * Determines the break level after this syllable
564      * This is a feature processor. A feature processor takes an item,
565      * performs some sort of processing on the item and returns an object.
566      */
567     public static class SylBreak implements FeatureProcessor {
568
569         /**
570          * Performs some processing on the given item.
571          *
572          * @param  syl  the item to process
573          *
574          * @return the break level after this syllable
575          *
576          * @throws ProcessException if an exception occurred during the
577          * processing
578          */
579         public String process(Item syl) throws ProcessException {
580             Item ss = syl.getItemAs(Relation.SYLLABLE_STRUCTURE);
581             if (ss == null) {
582                 return "1";
583             } else if (ss.getNext() != null) {
584                 return "0";
585             } else if (ss.getParent() == null) {
586                 return "1";
587             } else {
588                 return wordBreak(ss.getParent());
589             }
590         }
591     }
592
593
594
595     /**
596      * Determines the word break.
597      * This is a feature processor. A feature processor takes an item,
598      * performs some sort of processing on the item and returns an object.
599      */
600     public static class WordBreak implements FeatureProcessor {
601
602         /**
603          * Performs some processing on the given item.
604          *
605          * @param  word  the item to process
606          *
607          * @return the break level for this word
608          *
609          * @throws ProcessException if an exception occurred during the
610          * processing
611          */
612         public String process(Item word) throws ProcessException {
613             return wordBreak(word);
614         }
615     }
616
617     /**
618      * Determines the word punctuation.
619      * This is a feature processor. A feature processor takes an item,
620      * performs some sort of processing on the item and returns an object.
621      */
622     public static class WordPunc implements FeatureProcessor {
623
624         /**
625          * Performs some processing on the given item.
626          *
627          * @param  word  the item to process
628          *
629          * @return the punctuation for this word
630          *
631          * @throws ProcessException if an exception occurred during the
632          * processing
633          */
634         public String process(Item word) throws ProcessException {
635             return wordPunc(word);
636         }
637     }
638
639     /**
640      * Return consonant cplace 
641      *   l-labial a-alveolar p-palatal b-labio_dental d-dental v-velar
642      *
643      * This is a feature processor. A feature processor takes an item,
644      * performs some sort of processing on the item and returns an object.
645      */
646     public static class PH_CPlace implements FeatureProcessor {
647
648         /**
649          * Performs some processing on the given item.
650          *
651          * @param  item  the item to process
652          *
653          * @return consonant cplace
654          *
655          * @throws ProcessException if an exception occurred during the
656          * processing
657          */
658         public String process(Item item) throws ProcessException {
659             return getPhoneFeature(item, "cplace");
660         }
661     }
662
663     /**
664      * Return consonant type 
665      *   s-stop f-fricative a-affricative n-nasal * l-liquid
666      *
667      * This is a feature processor. A feature processor takes an item,
668      * performs some sort of processing on the item and returns an object.
669      */
670     public static class PH_CType implements FeatureProcessor {
671
672         /**
673          * Performs some processing on the given item.
674          *
675          * @param  item  the item to process
676          *
677          * @return consonant type
678          *
679          * @throws ProcessException if an exception occurred during the
680          * processing
681          */
682         public String process(Item item) throws ProcessException {
683             return getPhoneFeature(item, "ctype");
684         }
685     }
686
687     /**
688      * Return consonant voicing 
689      *   +=on -=off
690      *
691      * This is a feature processor. A feature processor takes an item,
692      * performs some sort of processing on the item and returns an object.
693      */
694     public static class PH_CVox implements FeatureProcessor {
695
696         /**
697          * Performs some processing on the given item.
698          *
699          * @param  item  the item to process
700          * 
701          * @return consonant voicing
702          *
703          * @throws ProcessException if an exception occurred during the
704          * processing
705          */
706         public String process(Item item) throws ProcessException {
707             return getPhoneFeature(item, "cvox");
708         }
709     }
710
711     /**
712      * Return vowel or consonant
713      *   +=on -=off
714      *
715      * This is a feature processor. A feature processor takes an item,
716      * performs some sort of processing on the item and returns an object.
717      */
718     public static class PH_VC implements FeatureProcessor {
719
720         /**
721          * Performs some processing on the given item.
722          *
723          * @param  item  the item to process
724          *
725          * @return vowel or consonant
726          *
727          * @throws ProcessException if an exception occurred during the
728          * processing
729          */
730         public String process(Item item) throws ProcessException {
731             return getPhoneFeature(item, "vc");
732         }
733     }
734
735     /**
736      * Return vowel frontness
737      *  1-front  2-mid 3-back
738      *
739      * This is a feature processor. A feature processor takes an item,
740      * performs some sort of processing on the item and returns an object.
741      */
742     public static class PH_VFront implements FeatureProcessor {
743
744         /**
745          * Performs some processing on the given item.
746          *
747          * @param  item  the item to process
748          *
749          * @return vowel frontness
750          *
751          * @throws ProcessException if an exception occurred during the
752          * processing
753          */
754         public String process(Item item) throws ProcessException {
755             return getPhoneFeature(item, "vfront");
756         }
757     }
758
759     /**
760      * Return vowel height
761      *   1-high 2-mid 3-low
762      *
763      * This is a feature processor. A feature processor takes an item,
764      * performs some sort of processing on the item and returns an object.
765      */
766     public static class PH_VHeight implements FeatureProcessor {
767
768         /**
769          * Performs some processing on the given item.
770          *
771          * @param  item  the item to process
772          * 
773          * @return vowel height
774          *
775          * @throws ProcessException if an exception occurred during the
776          * processing
777          */
778         public String process(Item item) throws ProcessException {
779             return getPhoneFeature(item, "vheight");
780         }
781     }
782
783
784     /**
785      * Return vowel length
786      *   s-short l-long d-dipthong a-schwa
787      *
788      * This is a feature processor. A feature processor takes an item,
789      * performs some sort of processing on the item and returns an object.
790      */
791     public static class PH_VLength implements FeatureProcessor {
792
793         /**
794          * Performs some processing on the given item.
795          *
796          * @param  item  the item to process
797          *
798          * @return vowel length
799          *
800          * @throws ProcessException if an exception occurred during the
801          * processing
802          */
803         public String process(Item item) throws ProcessException {
804             return getPhoneFeature(item, "vlng");
805         }
806     }
807
808
809
810
811     /**
812      * Return vowel rnd (lip rounding)
813      *   lip rounding  +=on -=off
814      *
815      * This is a feature processor. A feature processor takes an item,
816      * performs some sort of processing on the item and returns an object.
817      */
818     public static class PH_VRnd implements FeatureProcessor {
819
820         /**
821          * Performs some processing on the given item.
822          *
823          * @param  item  the item to process
824          *
825          * @return volwel rnd
826          *
827          * @throws ProcessException if an exception occurred during the
828          * processing
829          */
830         public String process(Item item) throws ProcessException {
831             return getPhoneFeature(item, "vrnd");
832         }
833     }
834
835     /**
836      * Determines the onset size of this syllable
837      * This is a feature processor. A feature processor takes an item,
838      * performs some sort of processing on the item and returns an object.
839      */
840     public static class SylOnsetSize implements FeatureProcessor {
841
842         /**
843          * Performs some processing on the given item.
844          *
845          * @param  syl  the item to process
846          *
847          * @return onset size of this syllable
848          *
849          * @throws ProcessException if an exception occurred during the
850          * processing
851          */
852         public String process(Item syl) throws ProcessException {
853             int count = 0;
854             Item daughter = syl.getItemAs(
855                 Relation.SYLLABLE_STRUCTURE).getDaughter();
856             while (daughter != null) {
857                 if ("+".equals(getPhoneFeature(daughter, "vc"))) {
858                     break;
859                 }
860                 count++;
861                 daughter = daughter.getNext();
862             }
863             return Integer.toString(rail(count));
864         }
865     }
866
867
868     /**
869      * Determines the coda size
870      * This is a feature processor. A feature processor takes an item,
871      * performs some sort of processing on the item and returns an object.
872      */
873     public static class SylCodaSize implements FeatureProcessor {
874
875         /**
876          * Performs some processing on the given item.
877          *
878          * @param  syl  the item to process
879          *
880          * @return coda size
881          *
882          * @throws ProcessException if an exception occurred during the
883          * processing
884          */
885         public String process(Item syl) throws ProcessException {
886             int count = 0;
887             Item daughter = syl.getItemAs(
888                 Relation.SYLLABLE_STRUCTURE).getLastDaughter();
889
890             while (daughter != null) {
891                 if ("+".equals(getPhoneFeature(daughter, "vc"))) {
892                     break;
893                 }
894
895                 daughter = daughter.getPrevious();
896                 count++;
897             }
898             return Integer.toString(rail(count));
899         }
900     }
901
902
903     /**
904      * Checks for fricative
905      * This is a feature processor. A feature processor takes an item,
906      * performs some sort of processing on the item and returns an object.
907      */
908     public static class SegCodaFric implements FeatureProcessor {
909
910         /**
911          * Performs some processing on the given item.
912          *
913          * @param  seg  the item to process
914          * 
915          * @return "1" if fricative; else "0"
916          *
917          * @throws ProcessException if an exception occurred during the
918          * processing
919          */
920         public String process(Item seg) throws ProcessException {
921             return segCodaCtype(seg, "f");
922         }
923     }
924
925     /**
926      * Checks for fricative
927      * This is a feature processor. A feature processor takes an item,
928      * performs some sort of processing on the item and returns an object.
929      */
930     public static class SegOnsetFric implements FeatureProcessor {
931
932         /**
933          * Performs some processing on the given item.
934          *
935          * @param  seg  the item to process
936          * 
937          * @return "1" if fricative; else "0"
938          *
939          * @throws ProcessException if an exception occurred during the
940          * processing
941          */
942         public String process(Item seg) throws ProcessException {
943             return segOnsetCtype(seg, "f");
944         }
945     }
946
947
948
949     /**
950      * Checks for coda stop
951      * This is a feature processor. A feature processor takes an item,
952      * performs some sort of processing on the item and returns an object.
953      */
954     public static class SegCodaStop implements FeatureProcessor {
955
956         /**
957          * Performs some processing on the given item.
958          *
959          * @param  seg  the item to process
960          *
961          * @return if coda stop "1"; otherwise "0"
962          *
963          * @throws ProcessException if an exception occurred during the
964          * processing
965          */
966         public String process(Item seg) throws ProcessException {
967             return segCodaCtype(seg, "s");
968         }
969     }
970
971     /**
972      * Checks for onset stop
973      * This is a feature processor. A feature processor takes an item,
974      * performs some sort of processing on the item and returns an object.
975      */
976     public static class SegOnsetStop implements FeatureProcessor {
977
978         /**
979          * Performs some processing on the given item.
980          *
981          * @param  seg  the item to process
982          *
983          * @return if Onset Stop "1"; otherwise "0"
984          *
985          * @throws ProcessException if an exception occurred during the
986          * processing
987          */
988         public String process(Item seg) throws ProcessException {
989             return segOnsetCtype(seg, "s");
990         }
991     }
992
993     /**
994      * Checks for coda nasal
995      * This is a feature processor. A feature processor takes an item,
996      * performs some sort of processing on the item and returns an object.
997      */
998     public static class SegCodaNasal implements FeatureProcessor {
999
1000         /**
1001          * Performs some processing on the given item.
1002          *
1003          * @param  seg  the item to process
1004          *
1005          * @return if coda stop "1"; otherwise "0"
1006          *
1007          * @throws ProcessException if an exception occurred during the
1008          * processing
1009          */
1010         public String process(Item seg) throws ProcessException {
1011             return segCodaCtype(seg, "n");
1012         }
1013     }
1014
1015     /**
1016      * Checks for onset nasal
1017      * This is a feature processor. A feature processor takes an item,
1018      * performs some sort of processing on the item and returns an object.
1019      */
1020     public static class SegOnsetNasal implements FeatureProcessor {
1021
1022         /**
1023          * Performs some processing on the given item.
1024          *
1025          * @param  seg  the item to process
1026          *
1027          * @return if Onset Stop "1"; otherwise "0"
1028          *
1029          * @throws ProcessException if an exception occurred during the
1030          * processing
1031          */
1032         public String process(Item seg) throws ProcessException {
1033             return segOnsetCtype(seg, "n");
1034         }
1035     }
1036
1037     /**
1038      * Checks for coda glide
1039      * This is a feature processor. A feature processor takes an item,
1040      * performs some sort of processing on the item and returns an object.
1041      */
1042     public static class SegCodaGlide implements FeatureProcessor {
1043
1044         /**
1045          * Performs some processing on the given item.
1046          *
1047          * @param  seg  the item to process
1048          *
1049          * @return if coda stop "1"; otherwise "0"
1050          *
1051          * @throws ProcessException if an exception occurred during the
1052          * processing
1053          */
1054         public String process(Item seg) throws ProcessException {
1055             if (segCodaCtype(seg, "r").equals("0")) {
1056                 return segCodaCtype(seg, "l");
1057             }
1058             return "1";
1059         }
1060     }
1061
1062     /**
1063      * Checks for onset glide
1064      * This is a feature processor. A feature processor takes an item,
1065      * performs some sort of processing on the item and returns an object.
1066      */
1067     public static class SegOnsetGlide implements FeatureProcessor {
1068
1069         /**
1070          * Performs some processing on the given item.
1071          *
1072          * @param  seg  the item to process
1073          *
1074          * @return if coda stop "1"; otherwise "0"
1075          *
1076          * @throws ProcessException if an exception occurred during the
1077          * processing
1078          */
1079         public String process(Item seg) throws ProcessException {
1080             if (segOnsetCtype(seg, "r").equals("0")) {
1081                 return segOnsetCtype(seg, "l");
1082             }
1083             return "1";
1084         }
1085     }
1086
1087
1088     /**
1089      * Checks for onset coda 
1090      * This is a feature processor. A feature processor takes an item,
1091      * performs some sort of processing on the item and returns an object.
1092      */
1093     public static class SegOnsetCoda implements FeatureProcessor {
1094
1095         /**
1096          * Performs some processing on the given item.
1097          *
1098          * @param  seg  the item to process
1099          *
1100          * @return if onset coda "1"; otherwise "0"
1101          *
1102          * @throws ProcessException if an exception occurred during the
1103          * processing
1104          */
1105         public String process(Item seg) throws ProcessException {
1106             Item s = seg.getItemAs(Relation.SYLLABLE_STRUCTURE);
1107             if (s == null) {
1108                 return "coda";
1109             }
1110
1111             s = s.getNext();
1112             while (s != null) {
1113                 if ("+".equals(getPhoneFeature(s, "vc"))) {
1114                     return "onset";
1115                 }
1116
1117                 s = s.getNext();
1118             }
1119             
1120             return "coda";
1121         }
1122     }
1123
1124     /**
1125      * Counts the number of phrases before this one.
1126      * This is a feature processor. A feature processor takes an item,
1127      * performs some sort of processing on the item and returns an object.
1128      */
1129     public static class SubPhrases implements FeatureProcessor {
1130
1131         /**
1132          * Performs some processing on the given item.
1133          *
1134          * @param  item  the item to process
1135          *
1136          * @return the number of phrases before this one
1137          *
1138          * @throws ProcessException if an exception occurred during the
1139          * processing
1140          */
1141         public String process(Item item) throws ProcessException {
1142             int count = 0;
1143             Item inPhrase  = SUB_PHRASE_PATH.findItem(item);
1144
1145             for (Item p = inPhrase; p != null; p = p.getPrevious() )  {
1146                 count++;
1147             }
1148             return Integer.toString(rail(count));
1149         }
1150     }
1151
1152     /**
1153      * Returns the duration of the given segment
1154      * This is a feature processor. A feature processor takes an item,
1155      * performs some sort of processing on the item and returns an object.
1156      */
1157     public static class SegmentDuration implements FeatureProcessor {
1158
1159         /**
1160          * Performs some processing on the given item.
1161          *
1162          * @param  seg  the item to process
1163          *
1164          * @return the duration of the segment as a string.
1165          *
1166          * @throws ProcessException if an exception occurred during the
1167          * processing
1168          */
1169         public String process(Item seg) throws ProcessException {
1170             if (seg == null) {
1171                 return "0";
1172             } else if (seg.getPrevious() == null) {
1173                 return seg.getFeatures().getObject("end").toString();
1174             } else {
1175                 return Float.toString(
1176                         seg.getFeatures().getFloat("end") -
1177                         seg.getPrevious().getFeatures().getFloat("end")
1178                    );
1179             }
1180         }
1181     }
1182
1183     /**
1184      * Gets the phoneset feature with the given name
1185      *
1186      * @param item item the phoneme of interest
1187      * @param featureName the feature of interest
1188      *
1189      * @return the phone feature for the item
1190      *
1191      */
1192
1193     public static String getPhoneFeature(Item item, String featureName) {
1194         Voice voice = item.getUtterance().getVoice();
1195         String feature = voice.getPhoneFeature(item.toString(), featureName);
1196         return feature;
1197     }
1198
1199     /**
1200      * Classifies the type of word break
1201      *
1202      * @param  item  the item to process
1203      *
1204      * @return  "4" for a big break, "3" for  a break; otherwise "1"
1205      *
1206      * @throws ProcessException if an exception occurred during the
1207      * processing
1208      */
1209     public static String wordBreak(Item item) throws ProcessException {
1210         Item ww = item.getItemAs(Relation.PHRASE);
1211         if (ww == null || ww.getNext() != null) {
1212             return "1";
1213         } else {
1214             String pname = ww.getParent().toString();
1215             if (pname.equals("BB")) {
1216                 return "4";
1217             } else if (pname.equals("B")) {
1218                 return "3";
1219             } else {
1220                 return "1";
1221             }
1222         }
1223     }
1224
1225     /**
1226      * Gets the punctuation associated with the word
1227      *
1228      * @param  item  the word to process
1229      *
1230      * @return  the punctuation associated with the word
1231      *
1232      * @throws ProcessException if an exception occurred during the
1233      * processing
1234      */
1235     public static String wordPunc(Item item) throws ProcessException {
1236         Item ww = item.getItemAs(Relation.TOKEN);
1237         if (ww != null && ww.getNext() != null) {
1238             return "";
1239         } else {
1240             if (ww != null && ww.getParent() != null) {
1241                 return ww.getParent().getFeatures().getString("punc");
1242             } else {
1243                 return "";
1244             }
1245         }
1246     }
1247
1248     /**
1249      * Tests the coda ctype of the given segment.
1250      *
1251      * @param seg the segment to test
1252      * @param ctype the ctype to check for
1253      * 
1254      * @return "1" on match "0" on no match
1255      */
1256     private static String segCodaCtype(Item seg, String ctype) {
1257         Item daughter 
1258             = seg.getItemAs(
1259                 Relation.SYLLABLE_STRUCTURE).getParent().getLastDaughter();
1260
1261         while (daughter != null) {
1262             if ("+".equals(getPhoneFeature(daughter, "vc"))) {
1263                 return "0";
1264             }
1265             if (ctype.equals(getPhoneFeature(daughter, "ctype"))) {
1266                 return "1";
1267             }
1268
1269             daughter = daughter.getPrevious();
1270         }
1271         return "0";
1272     }
1273
1274     /**
1275      * Tests the onset ctype of the given segment.
1276      *
1277      * @param  seg  the segment to test to process
1278      * @param ctype the ctype to check for
1279      *
1280      * @return if Onset Stop "1"; otherwise "0"
1281      *
1282      */
1283     private static  String segOnsetCtype(Item seg, String ctype) {
1284         Item daughter = seg.getItemAs(
1285             Relation.SYLLABLE_STRUCTURE).getParent().getDaughter();
1286
1287         while (daughter != null) {
1288             if ("+".equals(getPhoneFeature(daughter, "vc"))) {
1289                 return "0";
1290             }
1291             if (ctype.equals(getPhoneFeature(daughter, "ctype"))) {
1292                 return "1";
1293             }
1294
1295             daughter = daughter.getNext();
1296         }
1297         return "0";
1298     }
1299
1300     /**
1301      * Determines if the given item is accented
1302      *
1303      * @param item the item of interest
1304      *
1305      * @return <code>true</code> if the item is accented, otherwise
1306      * <code>false</code>
1307      */
1308     private static boolean isAccented(Item item) {
1309         return (item.getFeatures().isPresent("accent") ||
1310             item.getFeatures().isPresent("endtone"));
1311     }
1312
1313     /**
1314      * Rails an int. flite never returns an int more than 19 from
1315      * a feature processor, we duplicate that behavior
1316      * here so that our tests will match.
1317      *
1318      * @param val the value to rail
1319      *
1320      * @return val clipped to be betweein 0 and 19
1321      */
1322     private static int rail(int val) {
1323         return val > 19 ? 19 : val;
1324     }
1325 }