Edinburgh Speech Tools  2.4-release
 All Classes Functions Variables Typedefs Enumerations Enumerator Friends Pages
EST_Relation.cc
1 /*************************************************************************/
2 /* */
3 /* Centre for Speech Technology Research */
4 /* University of Edinburgh, UK */
5 /* Copyright (c) 1998 */
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 : February 1998 */
35 /*-----------------------------------------------------------------------*/
36 /* Generalised relations in utterances */
37 /* */
38 /*=======================================================================*/
39 #include <cstdlib>
40 #include <cstdio>
41 #include <iostream>
42 #include <fstream>
43 #include "ling_class/EST_Relation.h"
44 #include "ling_class/EST_Item.h"
45 #include "relation_io.h"
46 
47 VAL_REGISTER_CLASS(relation,EST_Relation)
48 
50 {
51  p_name = name;
52  p_head = 0;
53  p_tail = 0;
54  p_utt = 0;
55 }
56 
58 {
59  p_head = 0;
60  p_tail = 0;
61  p_utt = 0;
62 }
63 
64 void EST_Relation::copy(const EST_Relation &r)
65 {
66  // Do a *full* copy include the contents of all the items
67  // But not the name (?)
68  EST_String tmp_name;
69  p_name = r.p_name;
70  p_head = 0;
71  p_tail = 0;
72  p_utt = 0; // can't be in the same utterance as r
73 
74  tmp_name = f.S("name", "");
75  f = r.f;
76  f.set("name", tmp_name);
77 
78  if (r.root() != 0)
79  {
80  EST_Item i = *r.root();
81  EST_Item *to_root = append(&i);
82  copy_node_tree_contents(r.root(),to_root);
83  }
84 }
85 
86 EST_Item *EST_Relation::append(EST_Item *si)
87 {
88 
89  EST_Item *nn;
90 
91  if (this == 0)
92  {
93  EST_warning("EST_Relation: no relation to append to");
94  return 0;
95  }
96  else if (p_tail == 0)
97  {
98  nn = new EST_Item(this, si);
99  p_head = nn;
100  }
101  else
102  nn = p_tail->insert_after(si);
103  p_tail = nn;
104 
105 // if (!si->f_present("id") && utt())
106 // si->fset("id", utt()->next_id());
107 
108  return nn;
109 }
110 
111 EST_Item *EST_Relation::append()
112 {
113  return append(0);
114 }
115 
116 EST_Item *EST_Relation::prepend()
117 {
118  return prepend(0);
119 }
120 
121 EST_Item *EST_Relation::prepend(EST_Item *si)
122 {
123  EST_Item *nn;
124 
125  if (this == 0)
126  {
127  EST_warning("EST_Relation: no relation to prepend to");
128  return 0;
129  }
130  else if (p_head == 0)
131  {
132  nn = new EST_Item(this,si);
133  p_tail = nn;
134  }
135  else
136  nn = p_head->insert_before(si);
137  p_head = nn;
138 
139  return nn;
140 }
141 
143 {
144  clear();
145 }
146 
148 {
149  EST_Item *node;
150  int i;
151 
152  if (this == 0)
153  return 0;
154  for (i=0,node=p_head; node; node=node->next())
155  i++;
156  return i;
157 }
158 
160 {
161  for (EST_Item *s = head(); s; s = s->next())
162  s->evaluate_features();
163 }
164 
166 {
167  EST_Item *nn,*nnn;
168 
169  for (nn = p_head; nn != 0; nn = nnn)
170  {
171  nnn = nn->next();
172  delete nn;
173  }
174  p_head = p_tail = 0;
175 }
176 
178 {
179  if (p_head == node)
180  p_head = node->next();
181  if (p_tail == node)
182  p_tail = node->prev();
183  delete node;
184 }
185 
187 {
188  for (EST_Item *s = p_head; s; s = next_item(s))
189  s->f_remove(name);
190 }
191 
192 void copy_relation(const EST_Relation &from, EST_Relation &to)
193 {
194  // clone the relation structure from into to, deleting any existing
195  // nodes in to.
196 
197  to.clear();
198 
199  if (from.root() != 0)
200  {
201  EST_Item *to_root = to.append(from.root());
202  copy_node_tree(from.root(),to_root);
203  }
204 }
205 
206 EST_write_status EST_Relation::save(ostream &outf,
207  const EST_String &type,
208  bool evaluate_ff) const
209 {
210  if (type == "esps")
211  return save_esps_label(&outf,*this,evaluate_ff);
212  else if (type == "htk")
213  return save_htk_label(&outf,*this);
214  else
215  {
216  EST_warning("EST_Relation: unsupported type: \"%s\"", (const char *)type);
217  return write_fail;
218  }
219 }
220 
221 EST_write_status EST_Relation::save(const EST_String &filename,
222  const EST_String &type,
223  bool evaluate_ff) const
224 {
225  if (type == "esps")
226  return save_esps_label(filename,*this,evaluate_ff);
227  else if (type == "htk")
228  return save_htk_label(filename,*this);
229  else
230  {
231  EST_warning("EST_Relation: unsupported type: \"%s\"", (const char *)type);
232  return write_fail;
233  }
234 }
235 
236 EST_write_status EST_Relation::save(const EST_String &filename,
237  bool evaluate_ff) const
238 {
239  return save(filename,"esps",evaluate_ff);
240 }
241 
242 EST_write_status EST_Relation::save(ostream &outf,
243  EST_TKVL<void *,int> contents) const
244 {
245  EST_TKVL<void *,int> nodenames;
246  int node_count = 1;
247  outf << "Relation " << name() << " ; ";
248  f.save(outf);
249  outf << endl;
250  save_items(p_head,outf,contents,nodenames,node_count);
251  outf << "End_of_Relation" << endl;
252  return write_ok;
253 }
254 
255 EST_write_status EST_Relation::save_items(EST_Item *node,
256  ostream &outf,
257  EST_TKVL<void *,int> &cnames,
258  EST_TKVL<void *,int> &nodenames,
259  int &node_count) const
260 {
261  if (node != 0)
262  {
263  EST_Item *n = node;
264  int myname;
265 
266  while (n)
267  {
268  myname = node_count++;
269  nodenames.add_item(n,myname);
270  n = n->next();
271  }
272 
273  n = node;
274  while (n)
275  {
276  // This will need to be expanded if the we make Relations
277  // have more complex structures
278  save_items(n->down(),outf,cnames,nodenames,node_count);
279  outf << nodenames.val(n) << " " <<
280  (n->contents() == 0 ? 0 : cnames.val(n->contents())) << " " <<
281  (n->up() == 0 ? 0 : nodenames.val(n->up())) << " " <<
282  (n->down() == 0 ? 0 : nodenames.val(n->down())) << " " <<
283  (n->next() == 0 ? 0 : nodenames.val(n->next())) << " " <<
284  (n->prev() == 0 ? 0 : nodenames.val(n->prev())) << endl;
285  n = n->next();
286  }
287  }
288  return write_ok;
289 }
290 
291 #if 0
292 EST_read_status EST_Relation::load(EST_TokenStream &ts,
293  const EST_THash<int,EST_Val> &contents)
294 {
295  if (ts.get() != "Relation")
296  {
297  cerr << "load_relation: " << ts.pos_description() <<
298  " no new Relation" << endl;
299  return misc_read_error;
300  }
301  p_name = ts.get().string();
302  if (ts.get() != ";")
303  {
304  cerr << "load_relation: " << ts.pos_description() <<
305  " semicolon missing after Relation name \"" <<
306  p_name << "\"" << endl;
307  return misc_read_error;
308  }
309  if (f.load(ts) != format_ok)
310  return misc_read_error;
311  if (load_items(ts,contents) != format_ok)
312  return misc_read_error;
313 
314  return format_ok;
315 }
316 #endif
317 
319  const EST_TVector < EST_Item_Content * > &contents
320  )
321 {
322  if (ts.get() != "Relation")
323  {
324  cerr << "load_relation: " << ts.pos_description() <<
325  " no new Relation" << endl;
326  return misc_read_error;
327  }
328  p_name = ts.get().string();
329  if (ts.get() != ";")
330  {
331  cerr << "load_relation: " << ts.pos_description() <<
332  " semicolon missing after Relation name \"" <<
333  p_name << "\"" << endl;
334  return misc_read_error;
335  }
336  if (f.load(ts) != format_ok)
337  return misc_read_error;
338  if (load_items(ts,contents) != format_ok)
339  return misc_read_error;
340 
341  return format_ok;
342 }
343 
344 void EST_Relation::node_tidy_up_val(int &k, EST_Val &v)
345 {
346  // Called to delete the nodes in the hash table when a load
347  // fails
348  (void)k;
349  EST_Item *node = item(v);
350  node->u = 0;
351  node->d = 0;
352  node->n = 0;
353  node->p = 0;
354  delete node;
355 }
356 
357 void EST_Relation::node_tidy_up(int &k, EST_Item *node)
358 {
359  // Called to delete the nodes in the hash table when a load
360  // fails
361  (void)k;
362  node->u = 0;
363  node->d = 0;
364  node->n = 0;
365  node->p = 0;
366  delete node;
367 }
368 
369 #if 0
370 EST_read_status EST_Relation::load_items(EST_TokenStream &ts,
371  const EST_THash<int,EST_Val> &contents)
372 {
373  // Load a set of nodes from a TokenStream, the file contains node
374  // descriptions one per line as 5 ints, this nodes name, the
375  // stream item it is to be related to, then the name of the
376  // nodes above, below, before and after it.
377  EST_THash<int,EST_Val> nodenames(100);
378  EST_read_status r = format_ok;
379  EST_Item *node = 0;
380  EST_Relation *rel=NULL;
381 // int expect_links=0;
382 
383  while (ts.peek() != "End_of_Relation")
384  {
385  int name = atoi(ts.get().string());
386  int siname;
387 
388  node = get_item_from_name(nodenames,name);
389  if (!node)
390  EST_error("Unknown item %d", name);
391 
392  if (rel==NULL)
393  {
394  rel=node->relation();
395 // EST_String type = rel->f.S("type", "");
396 // expect_links = (type == "ladder");
397  }
398 
399  siname = atoi(ts.get().string());
400  if (siname != 0)
401  {
402  int found;
403  EST_Val v = contents.val(siname,found);
404  if (!found)
405  {
406  cerr << "load_nodes: " << ts.pos_description() <<
407  " node's item contents" << siname << " doesn't exist\n";
408  r = misc_read_error;
409  break;
410  }
411  else
412  node->set_contents(icontent(v));
413  }
414  // up down next previous
415  node->u = get_item_from_name(nodenames,atoi(ts.get().string()));
416  node->d = get_item_from_name(nodenames,atoi(ts.get().string()));
417  node->n = get_item_from_name(nodenames,atoi(ts.get().string()));
418  node->p = get_item_from_name(nodenames,atoi(ts.get().string()));
419 
420 
421  // Read ladder links
422 #if 0
423  if (expect_links)
424  {
425  int numlinks = atoi(ts.get().string());
426  // node->link_feats.set("num_links",numlinks);
427  for (int i=0;i<numlinks;++i)
428  {
429  EST_Item * item = get_item_from_name(nodenames,atoi(ts.get().string()));
430  node->link_feats.set_val("link" + itoString(i),est_val(item));
431  }
432  }
433 #endif
434  }
435 
436  ts.get(); // skip End_of_Relation
437 
438  if (r == format_ok)
439  {
440  if (node != 0) // at least one node
441  {
442  p_head = get_item_from_name(nodenames,1);
443  p_tail = p_head->last();
444  if (!p_head->verify())
445  {
446  cerr << "load_nodes: " << ts.pos_description() <<
447  " nodes do not form consistent graph" << endl;
448  r = misc_read_error;
449  }
450  }
451  }
452 
453  if (r != format_ok)
454  {
455  // failed to read this relation so clear the created nodes
456  // before returning, no idea what state the links are in so
457  // explicitly unlink them before deleting them
458 
459  nodenames.map(node_tidy_up_val);
460  }
461  return r;
462 }
463 #endif
464 
465 EST_read_status EST_Relation::load_items(EST_TokenStream &ts,
466  const EST_TVector < EST_Item_Content * > &contents
467  )
468 {
469  // Load a set of nodes from a TokenStream, the file contains node
470  // descriptions one per line as 5 ints, this nodes name, the
471  // stream item it is to be related to, then the name of the
472  // nodes above, below, before and after it.
473 
474  EST_TVector < EST_Item * > nodenames(100);
475  // EST_THash<int,EST_Val> nodenames(100);
476  EST_read_status r = format_ok;
477  EST_Item *node = 0;
478  EST_Relation *rel=NULL;
479 // int expect_links=0;
480 
481  while (ts.peek() != "End_of_Relation")
482  {
483  int name = atoi(ts.get().string());
484  int siname;
485 
486  node = get_item_from_name(nodenames,name);
487  if (!node)
488  EST_error("Unknown item %d", name);
489 
490  if (rel==NULL)
491  {
492  rel=node->relation();
493 // EST_String type = rel->f.S("type", "");
494 // expect_links = (type == "ladder");
495  }
496 
497  siname = atoi(ts.get().string());
498  if (siname != 0)
499  {
500  EST_Item_Content *c = contents(siname);
501  if (c==NULL)
502  {
503  cerr << "load_nodes: " << ts.pos_description() <<
504  " node's stream item " << siname << " doesn't exist\n";
505  r = misc_read_error;
506  break;
507  }
508  else
509  node->set_contents(c);
510  }
511  // up down next previous
512  node->u = get_item_from_name(nodenames,atoi(ts.get().string()));
513  node->d = get_item_from_name(nodenames,atoi(ts.get().string()));
514  node->n = get_item_from_name(nodenames,atoi(ts.get().string()));
515  node->p = get_item_from_name(nodenames,atoi(ts.get().string()));
516 
517 #if 0
518  // Read ladder links
519  if (expect_links)
520  {
521  int numlinks = atoi(ts.get().string());
522  // node->link_feats.set("num_links",numlinks);
523  for (int i=0;i<numlinks;++i)
524  {
525  EST_Item * item = get_item_from_name(nodenames,atoi(ts.get().string()));
526  // node->link_feats.set_val("link" + itoString(i),est_val(item));
527  }
528  }
529 #endif
530  }
531 
532  ts.get(); // skip End_of_Relation
533 
534  if (r == format_ok)
535  {
536  if (node != 0) // at least one node
537  p_head = get_item_from_name(nodenames,1);
538  p_tail = p_head->last();
539  if (!p_head->verify())
540  {
541  cerr << "load_nodes: " << ts.pos_description() <<
542  " nodes do not form consistent graph" << endl;
543  r = misc_read_error;
544  }
545  }
546 
547  if (r != format_ok)
548  {
549  // failed to read this relation so clear the created nodes
550  // before returning, no idea what state the links are in so
551  // explicitly unlink them before deleting them
552  for(int ni=0; ni<nodenames.length(); ni++)
553  {
554  EST_Item *node = nodenames(ni);
555  if (node != NULL)
556  node_tidy_up(ni, node);
557  }
558  }
559  return r;
560 }
561 
562 EST_Item *EST_Relation::get_item_from_name(EST_THash<int,EST_Val> &nodenames,
563  int name)
564 {
565  // Return node named by name or create a new one if it doesn't
566  // already exist
567  EST_Item *node;
568  int found;
569 
570  if (name == 0)
571  return 0;
572  EST_Val v = nodenames.val(name,found);
573  if (!found)
574  {
575  node = new EST_Item(this, 0);
576  nodenames.add_item(name,est_val(node));
577  }
578  else
579  node = item(v);
580  return node;
581 }
582 
583 EST_Item *EST_Relation::get_item_from_name(EST_TVector< EST_Item * > &nodenames,
584  int name)
585 {
586  // Return node named by name or create a new one if it doesn't
587  // already exist
588 
589  if (name == 0)
590  return 0;
591 
592  if (name >= nodenames.length())
593  {
594  nodenames.resize(name*2, 1);
595  }
596 
597  EST_Item *node = nodenames(name);
598  if (node==NULL)
599  {
600  node = new EST_Item(this, 0);
601  nodenames[name] = node;
602  }
603 
604  return node;
605 }
606 
607 EST_read_status EST_Relation::load(const EST_String &filename,
608  EST_TokenStream &ts,
609  const EST_String &type)
610 {
611  EST_read_status r;
612 
613  f.set("filename",filename);
614 
615  if (type == "esps")
616  r = load_esps_label(ts,*this);
617  else if (type == "ogi")
618  r = load_ogi_label(ts,*this);
619  else if (type == "htk")
620  r = load_sample_label(ts,*this,10000000);
621  else if ((type == "ascii") || (type == "timit"))
622  r = load_sample_label(ts,*this,1);
623  else if (type == "words")
624  r = load_words_label(ts,*this);
625  else // currently esps is the default
626  r = load_esps_label(ts,*this);
627 
628  return r;
629 }
630 
631 EST_read_status EST_Relation::load(const EST_String &filename,
632  const EST_String &type)
633 {
634  // Load an isolated relation from a file, assuming Xlabel format
635  EST_TokenStream ts;
636  EST_read_status r;
637 
638  if (((filename == "-") ? ts.open(cin) : ts.open(filename)) != 0)
639  {
640  cerr << "load_relation: can't open relation input file "
641  << filename << endl;
642  return misc_read_error;
643  }
644  r = load(filename, ts, type);
645 
646  ts.close();
647 
648  return r;
649 }
650 
651 int num_leaves(const EST_Item *h)
652 {
653  int count = 0;
654  EST_Item *n;
655 
656  for (n = h->first_leaf(); n != 0; n=n->next_leaf())
657  count++;
658  return count;
659 }
660 
661 EST_Item *EST_Relation::first_leaf() const
662 {
663  return head()->first_leaf();
664 }
665 
666 EST_Item *EST_Relation::last_leaf() const
667 {
668  return head()->last_leaf();
669 }
670 
671 EST_Utterance *get_utt(EST_Item *s)
672 {
673  // Occasionally you need to get the utterance from a stream_item
674  // This finds any relations in s and follows them to the utterance
675  // If there aren't any Relations the this streamitem isn't in an
676  // utterances
677 
678  if (s == 0)
679  return 0;
680  if (s->relation())
681  return s->relation()->utt();
682  else
683  return 0; // can't find an utterance
684 }
685 
686 EST_Relation &EST_Relation::operator=(const EST_Relation &s)
687 {
688  copy(s);
689  return *this;
690 }
691 
692 ostream& operator << (ostream &s, const EST_Relation &a)
693 {
694  s << a.f << endl;
695 
696  for (EST_Item *p = a.head(); p; p = p->next())
697  s << *p << endl;
698 
699  return s;
700 }
701 
702