1 /*************************************************************************/
2 /*                                                                       */
3 /*                Centre for Speech Technology Research                  */
4 /*                     University of Edinburgh, UK                       */
5 /*                       Copyright (c) 1996,1997                         */
6 /*                        All Rights Reserved.                           */
7 /*                                                                       */
8 /*  Permission is hereby granted, free of charge, to use and distribute  */
9 /*  this software and its documentation without restriction, including   */
10 /*  without limitation the rights to use, copy, modify, merge, publish,  */
11 /*  distribute, sublicense, and/or sell copies of this work, and to      */
12 /*  permit persons to whom this work is furnished to do so, subject to   */
13 /*  the following conditions:                                            */
14 /*   1. The code must retain the above copyright notice, this list of    */
15 /*      conditions and the following disclaimer.                         */
16 /*   2. Any modifications must be clearly marked as such.                */
17 /*   3. Original authors' names are not deleted.                         */
18 /*   4. The authors' names are not used to endorse or promote products   */
19 /*      derived from this software without specific prior written        */
20 /*      permission.                                                      */
21 /*                                                                       */
22 /*  THE UNIVERSITY OF EDINBURGH AND THE CONTRIBUTORS TO THIS WORK        */
23 /*  DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING      */
24 /*  ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT   */
25 /*  SHALL THE UNIVERSITY OF EDINBURGH NOR THE CONTRIBUTORS BE LIABLE     */
26 /*  FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES    */
27 /*  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN   */
28 /*  AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,          */
29 /*  ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF       */
30 /*  THIS SOFTWARE.                                                       */
31 /*                                                                       */
32 /*************************************************************************/
33 /*                      Author :  Alan W Black                           */
34 /*                      Date   :  May 1998                               */
35 /*-----------------------------------------------------------------------*/
36 /*                                                                       */
37 /* Basic builtin features                                                */
38 /*                                                                       */
39 /*=======================================================================*/
40 #include <cstdio>
41 #include "festival.h"
42 #include "modules.h"
43 
44 static EST_String stressname("stress");
45 static EST_Val val_string0("0");
46 static EST_Val val_string1("1");
47 static EST_Val val_int0(0);
48 static EST_Val val_int1(1);
49 static EST_Val default_val_float(0.0);
50 
ff_addr(EST_Item * i)51 static EST_Val ff_addr(EST_Item *i)
52 {
53     char a[1024];
54 
55     // The address of the contents so that the same item from different views
56     // have the same address
57     sprintf(a,"%p",i->contents());
58     return EST_Val(a);
59 }
60 
ff_segment_duration(EST_Item * s)61 static EST_Val ff_segment_duration(EST_Item *s)
62 {
63     EST_Item *n = as(s,"Segment");
64     if (n == 0)
65     {
66 	cerr << "Asked for segment duration of item not in Segment relation."
67 	    << endl;
68 	festival_error();
69     }
70     if (n->prev() == 0)
71 	return EST_Val(s->F("end", 0));
72     else
73 	return EST_Val(s->F("end", 0)-(n->prev()->F("end",0)));
74 }
75 
ff_syllable_duration(EST_Item * s)76 static EST_Val ff_syllable_duration(EST_Item *s)
77 {
78     EST_Item *n = as(s,"SylStructure");
79     if (n == 0)
80     {
81 	cerr << "Asked for syllable duration of item not in SylStructure relation."
82 	     << endl;
83 	festival_error();
84     }
85     else
86     {
87 	EST_Item *fd = daughter1(n);
88 	EST_Item *ld = fd->last();
89 
90 	if (ld == 0)
91 	    return val_int0;
92 	EST_Item *ps = as(fd,"Segment")->prev();
93 	if (ps == 0)
94 	    return ld->F("end",0);
95 	else
96 	    return EST_Val(ld->F("end",0)-ps->F("end",0));
97     }
98 
99     // dummy for stupid VC++ compiler
100     {
101 	EST_Val junk;
102 	return junk;
103     }
104 }
105 
ff_word_duration(EST_Item * s)106 static EST_Val ff_word_duration(EST_Item *s)
107 {
108     EST_Item *n = as(s,"SylStructure");
109     if (n == 0)
110     {
111 	cerr << "Asked for word duration of item not in SylStructure relation."
112 	    << endl;
113 	festival_error();
114     }
115     else
116     {
117 	EST_Item *fd = daughter1(daughter1(n));
118 	EST_Item *ld = daughtern(daughtern(n));
119 
120 	if (ld == 0)
121 	    return val_int0;
122 	EST_Item *ps = as(fd,"Segment")->prev();
123 	if (ps == 0)
124 	    return ld->F("end",0);
125 	else
126 	    return EST_Val(ld->F("end",0)-ps->F("end",0));
127     }
128     // dummy for stupid VC++ compiler
129     {
130       EST_Val junk;
131       return junk;
132     }
133 }
134 
ff_seg_start(EST_Item * s)135 static EST_Val ff_seg_start(EST_Item *s)
136 {
137     EST_Item *n = as(s,"Segment");
138     if (n->prev() == 0)
139 	return default_val_float;
140     else
141 	return n->prev()->F("end",0);
142 }
143 
ff_syl_start(EST_Item * s)144 static EST_Val ff_syl_start(EST_Item *s)
145 {
146     EST_Item *n = as(s,"SylStructure");
147     if (daughter1(n) == 0)
148 	return default_val_float;
149     else
150 	return ff_seg_start(daughter1(n));
151 }
152 
ff_word_start(EST_Item * s)153 static EST_Val ff_word_start(EST_Item *s)
154 {
155     EST_Item *n = as(s,"SylStructure");
156     if (daughter1(daughter1(n)) == 0)
157 	return default_val_float;
158     else
159 	return ff_seg_start(daughter1(daughter1(n)));
160 }
161 
ff_seg_end(EST_Item * s)162 static EST_Val ff_seg_end(EST_Item *s)
163 {
164     return s->F("end",0);
165 }
166 
ff_seg_mid(EST_Item * s)167 static EST_Val ff_seg_mid(EST_Item *s)
168 {
169     return EST_Val(((float)ff_seg_start(s)+(float)ff_seg_end(s))/2.0);
170 }
171 
ff_syl_end(EST_Item * s)172 static EST_Val ff_syl_end(EST_Item *s)
173 {
174     EST_Item *n = as(s,"SylStructure");
175     if (daughtern(n) == 0)
176 	return default_val_float;
177     else
178 	return ff_seg_end(daughtern(n));
179 }
180 
ff_word_end(EST_Item * s)181 static EST_Val ff_word_end(EST_Item *s)
182 {
183     EST_Item *n = as(s,"SylStructure");
184     if (daughtern(n) == 0)
185 	return default_val_float;
186     else
187 	return ff_syl_end(daughtern(n));
188 }
189 
ff_position_type(EST_Item * s)190 static EST_Val ff_position_type(EST_Item *s)
191 {
192     /* Position of syllable in this word: initial, mod, final */
193     EST_Item *nn = as(s,"SylStructure");
194 
195     if (nn == 0)  // its not really a syllable
196 	return EST_Val("single");
197     else if (nn->next() == 0)
198     {
199 	if (nn->prev() == 0)
200 	    return EST_Val("single");
201 	else
202 	    return EST_Val("final");
203     }
204     else if (nn->prev() == 0)
205 	return EST_Val("initial");
206     else
207 	return EST_Val("mid");
208 }
209 
ff_word_break(EST_Item * w)210 static EST_Val ff_word_break(EST_Item *w)
211 {
212     /* Break index of word */
213     EST_Item *nn = as(w,"Phrase");
214     static EST_Val val4 = EST_Val(4);
215     static EST_Val val3 = EST_Val(3);
216     static EST_Val val2 = EST_Val(2);
217 
218     if ((nn == 0) || (nn->next() != 0))
219 	return val_int1;
220     else
221     {
222 	EST_Item *p = parent(nn);
223 	if (p)
224 	{
225 	    if (p->name() == "BB")
226 		return val4;
227 	    else if (p->name() == "B")
228 		return val3;
229 	    else if (p->name() == "mB")
230 		return val2;
231 	    else
232 		return EST_Val(p->name());
233 
234 	}
235 	else
236 	    return val_int1;
237     }
238 }
239 
ff_syl_break(EST_Item * s)240 static EST_Val ff_syl_break(EST_Item *s)
241 {
242     // 0 internal syl end, 1 word end, 4 phrase end (ToBI 3 and 4)
243     EST_Item *nn = as(s,"SylStructure");
244 
245     if (nn == 0)
246 	return val_int1;  // no sylstructure so maybe its standalone
247     else if (nn->next() != 0)  // word internal
248 	return val_int0;
249     else if (parent(nn) == 0)  // not in a word -- strange
250 	return val_int1;
251     else
252 	return ff_word_break(parent(nn));  // take it from the word
253 }
254 
ff_old_syl_break(EST_Item * s)255 static EST_Val ff_old_syl_break(EST_Item *s)
256 {
257     // 0 internal syl end, 1 word end, 4 phrase end (ToBI 3 and 4)
258     // 2's and threes are promoted to 4s
259     EST_Item *nn = as(s,"SylStructure");
260     static EST_Val val4 = EST_Val(4);
261 
262     if (nn == 0)
263 	return val_int1;  // no sylstructure so maybe its standalone
264     else if (nn->next() != 0)  // word internal
265 	return val_int0;
266     else if (parent(nn) == 0)  // not in a word -- strange
267 	return val_int1;
268     else
269     {
270 	EST_Val v = ff_word_break(parent(nn));
271 	if ((v == 3) || (v == 2))
272 	    return val4;
273 	else
274 	    return v;
275     }
276 }
277 
ff_syl_accented(EST_Item * s)278 static EST_Val ff_syl_accented(EST_Item *s)
279 {
280     // t if syllable is accented or not
281     EST_Item *nn = as(s,"Intonation");
282 
283     if ((nn == 0) || (daughter1(nn) == 0))
284 	return val_int0;
285     else
286 	return val_int1;
287 }
288 
ff_tobi_accent(EST_Item * s)289 static EST_Val ff_tobi_accent(EST_Item *s)
290 {
291     // First tobi accent related to syllable
292     EST_Item *nn = as(s,"Intonation");
293     EST_Item *p;
294 
295     for (p=daughter1(nn); p; p=p->next())
296 	if (p->name().contains("*"))
297 	    return EST_Val(p->name());
298     return EST_Val("NONE");
299 }
300 
ff_tobi_endtone(EST_Item * s)301 static EST_Val ff_tobi_endtone(EST_Item *s)
302 {
303     // First tobi endtone (phrase accent or boundary tone)
304     EST_Item *nn = as(s,"Intonation");
305     EST_Item *p;
306 
307     for (p=daughter1(nn); p; p=p->next())
308     {
309 	EST_String l = p->name();
310 	if ((l.contains("%")) || (l.contains("-")))
311 	    return EST_Val(p->name());
312     }
313 
314     return EST_Val("NONE");
315 }
316 
ff_syl_accent(EST_Item * s)317 static EST_Val ff_syl_accent(EST_Item *s)
318 {
319     // (first) accent or NONE on given syllable
320     EST_Item *nn = as(s,"Intonation");
321 
322     if (daughter2(nn))
323 	return EST_Val("multi");
324     else if (daughter1(nn))
325 	return EST_Val(daughter1(nn)->name());
326     else
327 	return EST_Val("NONE");
328 }
329 
ff_syl_numphones(EST_Item * s)330 static EST_Val ff_syl_numphones(EST_Item *s)
331 {
332     // Number of phones in syllable
333     EST_Item *nn = as(s,"SylStructure");
334 
335     return EST_Val(daughter1(nn)->length());
336 }
337 
ff_word_numsyls(EST_Item * s)338 static EST_Val ff_word_numsyls(EST_Item *s)
339 {
340     // Number of syllable in word
341     EST_Item *nn = as(s,"SylStructure");
342 
343     return EST_Val(daughter1(nn)->length());
344 }
345 
ff_syl_onsetsize(EST_Item * s)346 static EST_Val ff_syl_onsetsize(EST_Item *s)
347 {
348     // number of segments in the onset
349     EST_Item *nn = as(s,"SylStructure");
350     EST_Item *p;
351     int size;
352 
353     for (p=daughter1(nn),size=0; p; p=p->next(),size++)
354 	if (ph_is_vowel(p->name()))
355 	    return EST_Val(size);
356 
357     return EST_Val(size);
358 
359 }
360 
ff_syl_vowel(EST_Item * s)361 static EST_Val ff_syl_vowel(EST_Item *s)
362 {
363     // the vowel in the syllable
364     EST_Item *nn = as(s,"SylStructure");
365     EST_Item *p;
366     int size;
367 
368     for (p=daughter1(nn),size=0; p; p=p->next(),size++)
369 	if (ph_is_vowel(p->name()))
370 	    return EST_Val(p->name());
371 
372     // no vowel
373     return EST_Val("novowel");
374 }
375 
ff_syl_codasize(EST_Item * s)376 static EST_Val ff_syl_codasize(EST_Item *s)
377 {
378     // number of segments in the coda
379     EST_Item *nn = as(s,"SylStructure");
380     EST_Item *p;
381     int size;
382 
383     for (p=daughter1(nn)->last(),size=1; p; p=p->prev(),size++)
384 	if (ph_is_vowel(p->name()))
385 	    return EST_Val(size);
386 
387     return EST_Val(size);
388 }
389 
ff_syl_pc_unvox(EST_Item * s)390 static EST_Val ff_syl_pc_unvox(EST_Item *s)
391 {
392     // Returns percentage of syllable from start to first voiced phone
393     EST_Item *nn = as(s,"SylStructure");
394     EST_Item *p,*ps;
395     float unvox,start = 0;
396 
397     if (daughter1(nn) == 0)
398 	return val_int0;  // no segments in syllable
399     else if ((ps = as(daughter1(nn),"Segment")->prev()) != 0)
400 	start = ps->F("end",0);
401     unvox = start;
402 
403     for (p=daughter1(nn); p != 0; p=p->next())
404     {
405 	if ((ph_is_vowel(p->name())) ||
406 	    (ph_is_voiced(p->name())))
407 	    break;
408 	unvox = p->F("end",0);
409     }
410 
411     return EST_Val((int)(((unvox-start)*100)/
412 			 (daughtern(nn)->F("end",0)-start)));
413 }
414 
ff_syl_vowel_start(EST_Item * s)415 static EST_Val ff_syl_vowel_start(EST_Item *s)
416 {
417     // Returns start time of vowel in syllable (or start of syllable)
418     EST_Item *nn = as(s,"SylStructure");
419     EST_Item *p;
420 
421     for (p=daughter1(nn); p != 0; p=p->next())
422     {
423 	if (ph_is_vowel(p->name()))
424 	    return EST_Val(ff_seg_start(p));
425     }
426     // There isn't a vowel, so just take start of syl
427     return EST_Val(ff_syl_start(p));
428 }
429 
ff_seg_onsetcoda(EST_Item * s)430 static EST_Val ff_seg_onsetcoda(EST_Item *s)
431 {
432     // onset if seg in onset, coda otherwise (vowel is in coda)
433     EST_Item *nn = as(s,"SylStructure");
434     EST_Item *p;
435 
436     for (p=nn->next(); p; p=p->next())
437 	if (ph_is_vowel(p->name()))
438 	    return EST_Val("onset");
439     return EST_Val("coda");
440 }
441 
ff_seg_onset_stop(EST_Item * s)442 static EST_Val ff_seg_onset_stop(EST_Item *s)
443 {
444     // 1 if onset of the syllable attached to this segment has a stop
445     EST_Item *nn = as(s,"SylStructure")->first();
446 
447     for ( ; nn ; nn=nn->next())
448     {
449 	if (ph_is_vowel(nn->name()))
450 	    return val_string0;
451 	if (ph_is_stop(nn->name()))
452 	    return val_string1;
453     }
454     return val_string0;
455 }
456 
ff_seg_coda_fric(EST_Item * s)457 static EST_Val ff_seg_coda_fric(EST_Item *s)
458 {
459     // 1 if coda of the syllable attached to this segment has a fricative
460     EST_Item *nn = as(s,"SylStructure")->last();
461 
462     for ( ; nn ; nn=nn->prev())
463     {
464 	if (ph_is_vowel(nn->name()))
465 	    return val_string0;
466 	if (ph_is_fricative(nn->name()))
467 	    return val_string1;
468     }
469     return val_string0;
470 }
471 
ff_seg_pos_in_syl(EST_Item * s)472 static EST_Val ff_seg_pos_in_syl(EST_Item *s)
473 {
474     // position of segment in syllable
475     EST_Item *nn = as(s,"SylStructure");
476     EST_Item *p;
477     int pos=0;
478 
479     for (p=nn->first(); p; p=p->next(),pos++)
480 	if (p == nn)
481 	    return EST_Val(pos);
482     // don't think you can get here
483     return EST_Val(pos);
484 }
485 
ff_seg_syl_initial(EST_Item * s)486 static EST_Val ff_seg_syl_initial(EST_Item *s)
487 {
488     // 1 if seg is syllable initial, 0 otherwise.
489     EST_Item *nn = as(s,"SylStructure");
490 
491     if (nn->prev() == 0)
492 	return val_string1;
493     else
494 	return val_string0;
495 }
496 
ff_seg_syl_final(EST_Item * s)497 static EST_Val ff_seg_syl_final(EST_Item *s)
498 {
499     // 1 if seg is syllable initial, 0 otherwise.
500     EST_Item *nn = as(s,"SylStructure");
501 
502     if (nn->next() == 0)
503 	return val_string1;
504     else
505 	return val_string0;
506 }
507 
ff_syl_pos_in_word(EST_Item * s)508 static EST_Val ff_syl_pos_in_word(EST_Item *s)
509 {
510     // position of syllable in word
511     EST_Item *nn = as(s,"SylStructure");
512     EST_Item *p;
513     int pos=0;
514 
515     for (p=nn->first(); p; p=p->next(),pos++)
516 	if (p == nn)
517 	    return EST_Val(pos);
518     // don't think you can get here
519     return EST_Val(pos);
520 }
521 
ff_pos_in_phrase(EST_Item * s)522 static EST_Val ff_pos_in_phrase(EST_Item *s)
523 {
524     // position of word in phrase
525     EST_Item *nn = as(s,"Phrase");
526     EST_Item *p;
527     int pos=0;
528 
529     for (p=nn->first(); p; p=p->next(),pos++)
530 	if (p == nn)
531 	    return EST_Val(pos);
532     // don't think you can get here
533     return EST_Val(pos);
534 }
535 
ff_num_break(EST_Item * s)536 static EST_Val ff_num_break(EST_Item *s)
537 {
538     // 1 if this word is at the end of a number group and followed by
539     // a new number group
540     EST_Item *nn = as(s,"Token");
541 
542     if ((nn->next() == 0) &&
543 	(parent(nn)->name().matches(RXdouble)) &&
544 	(parent(nn)->next()->name().matches(RXdouble)))
545 	return val_string1;
546     else
547 	return val_string0;
548 }
549 
ff_words_out(EST_Item * s)550 static EST_Val ff_words_out(EST_Item *s)
551 {
552     return EST_Val(as(s,"Phrase")->length());
553 }
554 
ff_syl_midpitch(EST_Item * s)555 static EST_Val ff_syl_midpitch(EST_Item *s)
556 {
557     // pitch of mid vowel in syllable
558     EST_Item *nn = as(s,"SylStructure");
559     EST_Item *p;
560 
561     for (p=daughter1(nn); p; p = p->next())
562     {
563 	if (ph_is_vowel(p->name()))
564 	    return ffeature(p,"R:Target.daughter1.f0");
565     }
566     // must be a silence or a syllabic consonant
567     return default_val_float;
568 }
569 
ff_syl_startpitch(EST_Item * s)570 static EST_Val ff_syl_startpitch(EST_Item *s)
571 {
572     // pitch at start of syllable
573     // average of first segment and previous segment target (if exists)
574 
575     float pt = ffeature(s,"R:SylStructure.daughter1.R:Segment.p.R:Target.daughter1.f0");
576     float tt = ffeature(s,"R:SylStructure.daughter1.R:Segment.R:Target.daughter1.f0");
577 
578     if (pt < 0.1)
579  	return EST_Val(tt);
580     else if (tt < 0.1)
581 	return EST_Val(pt);
582     else
583 	return EST_Val((tt+pt)/2.0);
584 }
585 
ff_syl_endpitch(EST_Item * s)586 static EST_Val ff_syl_endpitch(EST_Item *s)
587 {
588     // pitch at start of syllable
589     // average of first segment and previous segment target (if exists)
590 
591     float nt = ffeature(s,"R:SylStructure.daughtern.R:Segment.n.R:Target.daughter1.f0");
592     float tt = ffeature(s,"R:SylStructure.daughtern.R:Segment.R:Target.daughter1.f0");
593 
594     if (nt < 0.1)
595  	return EST_Val(tt);
596     else if (tt < 0.1)
597 	return EST_Val(nt);
598     else
599 	return EST_Val((tt+nt)/2.0);
600 }
601 
ff_seg_pitch(EST_Item * s)602 static EST_Val ff_seg_pitch(EST_Item *s)
603 {
604     // Return interpolated pitch at mid-point of s
605     EST_Item *t,*lastt;
606     float spoint,deltaf0,deltatime;
607     float smid = ff_seg_mid(s);
608     EST_Utterance *u = get_utt(s);
609 
610     for (lastt=t=u->relation("Target")->first_leaf();
611 	 next_leaf(t) != 0; t=next_leaf(t))
612     {
613 	if (smid <= t->F("pos",0))
614 	    break;
615 	lastt=t;
616     }
617 
618     if (lastt == 0)
619 	return EST_Val((float)0.0);
620 
621     deltaf0 = t->F("f0",0)-lastt->F("f0",0);
622     deltatime = t->F("pos",0) - lastt->F("pos",0);
623     if (deltatime <= 0)
624 	spoint = lastt->F("f0",0);
625     else
626 	spoint = lastt->F("f0",0) +
627 	    (deltaf0*((smid-lastt->F("pos",0))/deltatime));
628 
629     if (spoint > 35)
630 	return EST_Val(spoint);
631     else
632 	return EST_Val((float)0.0);
633 }
634 
ff_syl_in(EST_Item * s)635 static EST_Val ff_syl_in(EST_Item *s)
636 {
637     // Number of syllables to since last phrase break
638     EST_Item *nn = as(s,"Syllable");
639     // The first syllable in the phrase
640     EST_Item *fsyl =
641 	as(daughter1(as(parent(s,"SylStructure"),"Phrase")->first(),"SylStructure"),
642 	   "Syllable");
643     EST_Item *p;
644     int count;
645 
646     for (count=0,p=nn; p != 0; p=p->prev(),count++)
647 	if (p == fsyl)
648 	    return EST_Val(count);
649     return EST_Val(count);
650 }
651 
ff_syl_out(EST_Item * s)652 static EST_Val ff_syl_out(EST_Item *s)
653 {
654     // Number of syllables since last phrase break
655     EST_Item *nn = as(s,"Syllable");
656     // The last syllable in the phrase
657     EST_Item *lsyl =
658 	as(daughtern(as(parent(s,"SylStructure"),"Phrase")->last(),"SylStructure"),
659 	   "Syllable");
660     EST_Item *p;
661     int count;
662 
663     for (count=0,p=nn; p != 0; p=p->next(),count++)
664 	if (p == lsyl)
665 	    return EST_Val(count);
666     return EST_Val(count);
667 }
668 
ff_ssyl_in(EST_Item * s)669 static EST_Val ff_ssyl_in(EST_Item *s)
670 {
671     // Number of stressed syllables since last phrase break
672     EST_Item *nn = as(s,"Syllable");
673     EST_Item *fsyl =
674 	as(daughter1(as(parent(s,"SylStructure"),"Phrase")->first(),"SylStructure"),
675 	   "Syllable");
676     EST_Item *p;
677     int count;
678 
679     if (nn == fsyl) return val_int0;
680     for (count=0,p=nn->prev(); (p != 0) && (p != fsyl); p = p->prev())
681 	if (p->F(stressname,0) == 1)
682 	    count ++;
683     return EST_Val(count);
684 }
685 
686 // This function is modified version of ff_ssyl_in().
687 // If first syllable in the current phrase is stressed syllable,
688 // ff_ssyl_in() does not count it as stressed syllable.
689 // In the following function, this problem is fixed.
ff_ssyl_in_modified_version(EST_Item * s)690 static EST_Val ff_ssyl_in_modified_version(EST_Item *s)
691 {
692     // Number of stressed syllables since last phrase break
693     EST_Item *nn = as(s,"Syllable");
694     EST_Item *fsyl =
695 	as(daughter1(as(parent(s,"SylStructure"),"Phrase")->first(),"SylStructure"),
696 	   "Syllable");
697     EST_Item *p;
698     int count;
699 
700     if (nn == fsyl) return val_int0;
701     for (count=0,p=nn->prev(); (p != 0); p = p->prev())
702     {
703 	if (p->F(stressname,0) == 1)
704 	    count ++;
705 	if (p == fsyl) break;
706     }
707     return EST_Val(count);
708 }
709 
ff_ssyl_out(EST_Item * s)710 static EST_Val ff_ssyl_out(EST_Item *s)
711 {
712     // Number of stressed syllables to next phrase break
713     EST_Item *nn = as(s,"Syllable");
714     // The last syllable in the phrase
715     EST_Item *lsyl =
716 	as(daughtern(as(parent(s,"SylStructure"),"Phrase")->last(),"SylStructure"),
717 	   "Syllable");
718     EST_Item *p;
719     int count;
720 
721     if (nn == lsyl) return val_int0;
722     for (count=0,p=nn->next(); (p != 0); p=p->next())
723     {
724 	if (p->F(stressname,0) == 1)
725 	    count ++;
726 	if (p == lsyl) break;
727     }
728     return EST_Val(count);
729 }
730 
ff_asyl_in(EST_Item * s)731 static EST_Val ff_asyl_in(EST_Item *s)
732 {
733     // Number of accented syllables since last phrase break
734     EST_Item *nn = as(s,"Syllable");
735     // The first syllable in the phrase
736     EST_Item *fsyl =
737 	as(daughter1(as(parent(s,"SylStructure"),"Phrase")->first(),"SylStructure"),
738 	   "Syllable");
739     EST_Item *p;
740     int count;
741 
742     if (nn == fsyl) return val_int0;
743     for (count=0,p=nn->prev(); (p != 0) && (p != fsyl); p = p->prev())
744 	if (ff_syl_accented(p) == 1)
745 	    count ++;
746     return EST_Val(count);
747 }
748 
749 // This function is modified version of ff_asyl_in().
750 // If first syllable in the current phrase is accented syllable,
751 // ff_asyl_in() does not count it as accented syllable.
752 // In the following function, this problem is fixed.
ff_asyl_in_modified_version(EST_Item * s)753 static EST_Val ff_asyl_in_modified_version(EST_Item *s)
754 {
755     // Number of accented syllables since last phrase break
756     EST_Item *nn = as(s,"Syllable");
757     // The first syllable in the phrase
758     EST_Item *fsyl =
759 	as(daughter1(as(parent(s,"SylStructure"),"Phrase")->first(),"SylStructure"),
760 	   "Syllable");
761     EST_Item *p;
762     int count;
763 
764     if (nn == fsyl) return val_int0;
765     for (count=0,p=nn->prev(); (p != 0); p = p->prev())
766     {
767 	if (ff_syl_accented(p) == 1)
768 	    count ++;
769 	if (p == fsyl) break;
770     }
771     return EST_Val(count);
772 }
773 
ff_asyl_out(EST_Item * s)774 static EST_Val ff_asyl_out(EST_Item *s)
775 {
776     // Number of accented syllables to next phrase break
777     EST_Item *nn = as(s,"Syllable");
778     // The last syllable in the phrase
779     EST_Item *lsyl =
780 	as(daughtern(as(parent(s,"SylStructure"),"Phrase")->last(),"SylStructure"),
781 	   "Syllable");
782     EST_Item *p;
783     int count;
784 
785     if (nn == lsyl) return val_int0;
786     for (count=0,p=nn->next(); (p != 0); p=p->next())
787     {
788 	if (ff_syl_accented(p) == 1)
789 	    count ++;
790 	if (p == lsyl) break;
791     }
792     return EST_Val(count);
793 }
794 
ff_last_accent(EST_Item * s)795 static EST_Val ff_last_accent(EST_Item *s)
796 {
797     // Number of syllables since last accented syllable
798     EST_Item *nn = as(s,"Syllable");
799     EST_Item *p;
800     int count;
801 
802     for (count=0,p=nn->prev(); p != 0; p=p->prev(),count++)
803 	if (ff_syl_accented(p) == 1)
804 	    return EST_Val(count);
805 
806     return EST_Val(count);
807 }
808 
ff_next_accent(EST_Item * s)809 static EST_Val ff_next_accent(EST_Item *s)
810 {
811     // Number of syllables to next accented syllable
812     EST_Item *nn = as(s,"Syllable");
813     EST_Item *p;
814     int count;
815 
816     for (count=0,p=nn->next(); p != 0; p=p->next(),count++)
817 	if (ff_syl_accented(p) == 1)
818 	    return EST_Val(count);
819 
820     return EST_Val(count);
821 }
822 
ff_sub_phrases(EST_Item * s)823 static EST_Val ff_sub_phrases(EST_Item *s)
824 {
825     // Number of non-major phrase breaks since last major phrase break
826     EST_Item *nn = parent(parent(s,"SylStructure"),"Phrase");
827     EST_Item *p;
828     int count;
829 
830     for (count=0,p=nn->prev(); p != 0; p=p->prev())
831     {
832 	if (p->name() == "BB")
833 	    return EST_Val(count);
834 	count ++;
835     }
836 
837     return EST_Val(count);
838 }
839 
festival_ff_init(void)840 void festival_ff_init(void)
841 {
842 
843     festival_def_nff("segment_duration","Segment",ff_segment_duration,
844     "Segment.segment_duration\n\
845   The duration of the given stream item calculated as the end of this\n\
846   item minus the end of the previous item in the Segment relation.");
847     festival_def_nff("syllable_duration","Syllable",ff_syllable_duration,
848     "Syllable.syllable_duration\n\
849   The duration of the given stream item calculated as the end of last\n\
850   daughter minus the end of previous item in the Segment relation of the\n\
851   first duaghter.");
852     festival_def_nff("word_duration","Word",ff_word_duration,
853     "Word.word_duration\n\
854   The duration of the given stream item.  This is defined as the end of\n\
855   last segment in the last syllable (via the SylStructure relation) minus\n\
856   the segment immediate preceding the first segment in the first syllable.");
857     festival_def_nff("segment_start","Segment",ff_seg_start,
858     "Segement.segment_start\n\
859   The start time of the given segment.");
860     festival_def_nff("segment_mid","Segment",ff_seg_mid,
861     "Segement.segment_mid\n\
862   The middle time of the given segment.");
863     festival_def_nff("syllable_start","Syllable",ff_syl_start,
864     "Syllable.syllable_start\n\
865   The start time of the given syllable.");
866     festival_def_nff("word_start","Word",ff_word_start,
867     "Word.word_start\n\
868   The start time of the given word.");
869     festival_def_nff("segment_end","Segment",ff_seg_end,
870     "Segment.segment_end\n\
871   The end time of the given segment.");
872     festival_def_nff("syllable_end","Syllable",ff_syl_end,
873     "Syllable.syllable_end\n\
874   The end time of the given syllable.");
875     festival_def_nff("word_end","Word",ff_word_end,
876     "Word.word_end\n\
877   The end time of the given word.");
878     festival_def_nff("addr","ANY",ff_addr,
879     "ANY.addr\n\
880   Returned by popular demand, returns the address of given item that\n\
881   is guaranteed unique for this session.");
882 
883     festival_def_nff("accented","Syllable",ff_syl_accented,
884     "Syllable.accented\n\
885   Returns 1 if syllable is accented, 0 otherwise.  A syllable is\n\
886   accented if there is at least one IntEvent related to it.");
887     festival_def_nff("syl_accent","Syllable",ff_syl_accent,
888     "Syllable.syl_accent\n\
889   Returns the name of the accent related to the syllable.  NONE is returned\n\
890   if there are no accents, and multi is returned if there is more than one.");
891     festival_def_nff("tobi_accent","Syllable",ff_tobi_accent,
892     "Syllable.tobi_accent\n\
893   Returns the ToBI accent related to syllable.  ToBI accents are\n\
894   those which contain a *.  NONE is returned if there are none.  If\n\
895   there is more than one ToBI accent related to this syllable the\n\
896   first one is returned.");
897     festival_def_nff("tobi_endtone","Syllable",ff_tobi_endtone,
898     "Syllable.tobi_endtone\n\
899   Returns the ToBI endtone related to syllable.  ToBI end tones are\n\
900   those IntEvent labels which contain a % or a - (i.e. end tones or\n\
901   phrase accents).  NONE is returned if there are none.  If\n\
902   there is more than one ToBI end tone related to this syllable the\n\
903   first one is returned.");
904     festival_def_nff("syl_onsetsize","Syllable",ff_syl_onsetsize,
905     "Syllable.syl_onsetsize\n\
906   Returns the number of segments before the vowel in this syllable.  If\n\
907   there is no vowel in the syllable this will return the total number\n\
908   of segments in the syllable.");
909     festival_def_nff("syl_vowel","Syllable",ff_syl_vowel,
910     "Syllable.syl_vowel\n\
911   Returns the name of the vowel within this syllable.  Note this is not\n\
912   the general form you probably want.  You can't refer to ph_* features \n\
913   of this.  Returns \"novowel\" is no vowel can be found.");
914     festival_def_nff("syl_codasize","Syllable",ff_syl_codasize,
915     "Syllable.syl_codasize\n\
916   Returns the number of segments after the vowel in this syllable.  If\n\
917   there is no vowel in the syllable this will return the total number\n\
918   of segments in the syllable." );
919     festival_def_nff("seg_onsetcoda","Segment",ff_seg_onsetcoda,
920     "Segment.seg_onsetcoda\n\
921   Returns onset if this segment is before the vowel in the syllable it\n\
922   is contained within.  Returns coda if it is the vowel or after.  If\n\
923   the segment is not in a syllable it returns onset.");
924     festival_def_nff("seg_onset_stop","Segment",ff_seg_onset_stop,
925     "Segment.seg_onset_stop\n\
926   Returns 1 if onset of the syllable this segment is in contains a stop.\n\
927   0 otherwise.");
928     festival_def_nff("seg_coda_fric","Segment",ff_seg_coda_fric,
929     "Segment.seg_coda_fric\n\
930   Returns 1 if coda of the syllable this segment is in contains a fricative.\n\
931   0 otherwise.");
932     festival_def_nff("syl_numphones","Syllable",ff_syl_numphones,
933     "Syllable.syl_numphones\n\
934   Returns number of phones in syllable.");
935     festival_def_nff("syl_pc_unvox","Syllable",ff_syl_pc_unvox,
936     "Syllable.syl_pc_unvox\n\
937   Percentage of total duration of unvoiced segments from\n\
938   start of syllable. (i.e. percentage to start of first voiced segment)");
939     festival_def_nff("syl_vowel_start","Syllable",ff_syl_vowel_start,
940     "Syllable.syl_vowel_start\n\
941   Start position of vowel in syllable.  If there is no vowel the start\n\
942   position of the syllable is returned.");
943     festival_def_nff("syl_midpitch","Syllable",ff_syl_midpitch,
944     "Syllable.syl_midpitch\n\
945   Pitch at the mid vowel of this syllable.");
946     festival_def_nff("syl_startpitch","Syllable",ff_syl_startpitch,
947     "Syllable.syl_startpitch\n\
948   Pitch at the start of this syllable.");
949     festival_def_nff("syl_endpitch","Syllable",ff_syl_endpitch,
950     "Syllable.syl_endpitch\n\
951   Pitch at the end of this syllable.");
952     festival_def_nff("seg_pitch","Segment",ff_seg_pitch,
953     "Segment.seg_pitch\n\
954   Pitch at the middle of this segment.");
955 
956     festival_def_nff("syl_in","Syllable",ff_syl_in,
957     "Syllable.syl_in\n\
958   Returns number of syllables since last phrase break.  This is 0 if\n\
959   this syllable is phrase initial.");
960     festival_def_nff("syl_out","Syllable",ff_syl_out,
961     "Syllable.syl_out\n\
962   Returns number of syllables to next phrase break.  This is 0 if\n\
963   this syllable is phrase final.");
964     festival_def_nff("ssyl_in","Syllable",ff_ssyl_in,
965     "Syllable.ssyl_in\n\
966   Returns number of stressed syllables since last phrase break, not\n\
967   including this one.");
968     festival_def_nff("ssyl_in_modified_version","Syllable",ff_ssyl_in_modified_version,
969     "Syllable.ssyl_in_modified_version\n\
970   Returns number of stressed syllables since last phrase break, not\n\
971   including this one.");
972     festival_def_nff("ssyl_out","Syllable",ff_ssyl_out,
973     "Syllable.ssyl_out\n\
974   Returns number of stressed syllables to next phrase break, not including\n\
975   this one.");
976     festival_def_nff("asyl_in","Syllable",ff_asyl_in,
977     "Syllable.asyl_in\n\
978   Returns number of accented syllables since last phrase break, not\n\
979   including this one.  Accentedness is as defined by the syl_accented\n\
980   feature.");
981     festival_def_nff("asyl_in_modified_version","Syllable",ff_asyl_in_modified_version,
982     "Syllable.asyl_in_modified_version\n\
983   Returns number of accented syllables since last phrase break, not\n\
984   including this one.  Accentedness is as defined by the syl_accented\n\
985   feature.");
986     festival_def_nff("asyl_out","Syllable",ff_asyl_out,
987     "Syllable.asyl_out\n\
988   Returns number of accented syllables to the next phrase break, not\n\
989   including this one.  Accentedness is as defined by the syl_accented\n\
990   feature.");
991     festival_def_nff("last_accent","Syllable",ff_last_accent,
992     "Syllable.last_accent\n\
993   Returns the number of syllables since last accented syllable.");
994     festival_def_nff("next_accent","Syllable",ff_next_accent,
995     "Syllable.next_accent\n\
996   Returns the number of syllables to the next accented syllable.");
997     festival_def_nff("sub_phrases","Syllable",ff_sub_phrases,
998     "Syllable.sub_phrases\n\
999   Returns the number of non-major phrase breaks since last major\n\
1000   phrase break.  Major phrase breaks are 4, as returned by syl_break,\n\
1001   minor phrase breaks are 2 and 3.");
1002     festival_def_nff("syl_break","Syllable",ff_syl_break,
1003     "Syllable.syl_break\n\
1004   The break level after this syllable.  Word internal is syllables\n\
1005   return 0, non phrase final words return 1.  Final syllables in \n\
1006   phrase final words return the name of the phrase they are related to.\n\
1007   Note the occasional \"-\" that may appear of phrase names is removed\n\
1008   so that this feature function returns a number in the range 0,1,2,3,4.");
1009     festival_def_nff("old_syl_break","Syllable",ff_old_syl_break,
1010     "Syllable.old_syl_break\n\
1011   Like syl_break but 2 and 3 are promoted to 4 (to be compatible with\n\
1012   some older models.");
1013     festival_def_nff("pos_in_syl","Segment",ff_seg_pos_in_syl,
1014     "Segment.pos_in_syl\n\
1015   The position of this segment in the syllable it is related to.  The index\n\
1016   counts from 0.  If this segment is not related to a syllable this \n\
1017   returns 0.");
1018     festival_def_nff("syl_initial","Segment",ff_seg_syl_initial,
1019     "Segment.syl_initial\n\
1020   Returns 1 if this segment is the first segment in the syllable it\n\
1021   is related to, or if it is not related to any syllable.");
1022     festival_def_nff("syl_final","Segment",ff_seg_syl_final,
1023     "Segment.syl_final\n\
1024   Returns 1 if this segment is the last segment in the syllable it\n\
1025   is related to, or if it is not related to any syllable.");
1026     festival_def_nff("pos_in_word","Syllable",ff_syl_pos_in_word,
1027     "Syllable.pos_in_word\n\
1028   The position of this syllable in the word it is related to.  The index\n\
1029   counts from 0.  If this syllable is not related to a word then 0 is\n\
1030   returned.");
1031     festival_def_nff("word_numsyls","Word",ff_word_numsyls,
1032     "Word.word_numsyls\n\
1033   Returns number of syllables in a word.");
1034     festival_def_nff("word_break","Word",ff_word_break,
1035     "Word.word_break\n\
1036   The break level after this word.  Non-phrase final words return 1\n\
1037   Phrase final words return the name of the phrase they are in.");
1038     festival_def_nff("pos_in_phrase","Word",ff_pos_in_phrase,
1039     "Word.pos_in_phrase\n\
1040   The position of this word in the phrase this word is in.");
1041     festival_def_nff("num_break","Word",ff_num_break,
1042     "Word.num_break\n\
1043   1 if this is the last word in a numeric token and it is followed by\n\
1044   a numeric token.");
1045     festival_def_nff("words_out","Word",ff_words_out,
1046     "Word.words_out\n\
1047   Number of words to end of this phrase.");
1048     festival_def_nff("position_type","Syllable",ff_position_type,
1049     "Syllable.position_type\n\
1050   The type of syllable with respect to the word it it related to.  This\n\
1051   may be any of: single for single syllable words, initial for word\n\
1052   initial syllables in a poly-syllabic word, final for word final\n\
1053   syllables in poly-syllabic words, and mid for syllables within \n\
1054   poly-syllabic words.");
1055 
1056 }
1057