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