1/*---------------------------------------------------------------------------*
2 *  srec_results.c  *
3 *                                                                           *
4 *  Copyright 2007, 2008 Nuance Communciations, Inc.                               *
5 *                                                                           *
6 *  Licensed under the Apache License, Version 2.0 (the 'License');          *
7 *  you may not use this file except in compliance with the License.         *
8 *                                                                           *
9 *  You may obtain a copy of the License at                                  *
10 *      http://www.apache.org/licenses/LICENSE-2.0                           *
11 *                                                                           *
12 *  Unless required by applicable law or agreed to in writing, software      *
13 *  distributed under the License is distributed on an 'AS IS' BASIS,        *
14 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
15 *  See the License for the specific language governing permissions and      *
16 *  limitations under the License.                                           *
17 *                                                                           *
18 *---------------------------------------------------------------------------*/
19
20#include"passert.h"
21
22#include"portable.h"
23#include"srec.h"
24#include"search_network.h"
25#include"srec_stats.h"
26#if USE_COMP_STATS
27#include"comp_stats.h"
28#endif
29#include"srec_results.h"
30
31static srec* WHICH_RECOG(multi_srec* recm)
32{
33  srec* return_rec = NULL;
34  costdata current_best_cost = MAXcostdata;
35  int i = 0;
36#if DO_ALLOW_MULTIPLE_MODELS
37  for (i = 0; i < recm->num_activated_recs; i++)
38  {
39#endif
40    if (current_best_cost > recm->rec[i].current_best_cost)
41    {
42      current_best_cost = recm->rec[i].current_best_cost;
43      return_rec = &recm->rec[i];
44    }
45#if DO_ALLOW_MULTIPLE_MODELS
46  }
47#endif
48  return return_rec;
49}
50
51int srec_get_bestcost_recog_id(multi_srec* recm, int* id)
52{
53  srec* rec = WHICH_RECOG(recm);
54  if (!rec) *id = -1;
55  else *id = rec->id;
56  return 0;
57}
58
59void srec_result_strip_slot_markers(char* result)
60{
61  if (!result) return;
62  else
63  {
64    char *p = result, *q = p;
65    for (; (*q = *p); q++, p++)
66    {
67      if (p[0] == IMPORTED_RULES_DELIM && (p[2] == ' ' || p[2] == '\0'))
68      {
69        p += 2;
70        *q = *p;
71      }
72    }
73  }
74}
75
76int srec_has_results(multi_srec* recm)
77{
78  srec* rec = WHICH_RECOG(recm);
79  frameID end_frame;
80  if (!rec)
81    return 0;
82  end_frame = rec->current_search_frame;
83  if (!rec->srec_ended)
84    return 0;
85  if (rec->word_lattice->words_for_frame[end_frame] != MAXwtokenID)
86    return 1;
87  if (rec->astar_stack->num_complete_paths)
88    return 1;
89  return 0;
90}
91
92int srec_clear_results(multi_srec* recm)
93{
94  srec* rec = WHICH_RECOG(recm);
95  frameID ifr;
96  SREC_STATS_SHOW();
97  SREC_STATS_CLEAR();
98
99  if (!rec)
100    return 1;
101  astar_stack_clear(rec->astar_stack);
102  for (ifr = 0; ifr <= rec->current_search_frame; ifr++)
103    rec->word_lattice->words_for_frame[ifr] = MAXwtokenID;
104
105  return 0;
106}
107
108void* srec_nbest_prepare_list(multi_srec* recm, int n, asr_int32_t* bestcost)
109{
110  int rc;
111  srec* rec = WHICH_RECOG(recm);
112  AstarStack* stack = rec ? rec->astar_stack : 0;
113
114  if (!stack)
115    return NULL;
116#if USE_COMP_STATS
117  start_cs_clock1(&comp_stats->astar);
118#endif
119  rc = astar_stack_prepare(stack, n, rec);
120  if (rc)
121  {
122    *bestcost = MAXbcostdata;
123    return (void*)rec;
124  }
125  astar_stack_do_backwards_search(rec, n);
126#if USE_COMP_STATS
127  end_cs_clock1(&comp_stats->astar, 1);
128#endif
129  if (stack->num_complete_paths)
130  {
131    *bestcost = stack->complete_paths[0]->costsofar;
132  }
133  else
134  {
135    *bestcost = MAXbcostdata;
136  }
137
138  return (void*)(rec);
139}
140
141void srec_nbest_destroy_list(void* rec_void)
142{
143  srec* rec = (srec*)rec_void;
144  AstarStack* stack = rec ? rec->astar_stack : 0;
145  astar_stack_clear(stack);
146}
147
148int srec_nbest_get_num_choices(void* rec_void)
149{
150  srec* rec = (srec*)rec_void;
151  AstarStack* stack = rec ? rec->astar_stack : 0;
152  return stack ? stack->num_complete_paths : 0;
153}
154
155int srec_nbest_put_confidence_value(void* rec_void, int choice, int confidence_value)
156{
157  srec* rec = (srec*)rec_void;
158  AstarStack* stack = rec ? rec->astar_stack : 0;
159  if (!stack)
160  {
161	  return 1;
162  }
163  else
164  {
165  stack->complete_path_confidences[choice] = confidence_value;
166  return 0;
167  }
168}
169
170int srec_nbest_get_confidence_value(void* rec_void, int choice)
171{
172  srec* rec = (srec*)rec_void;
173  AstarStack* stack = rec ? rec->astar_stack : 0;
174  return stack->complete_path_confidences[choice];
175}
176
177int srec_nbest_fix_homonym_confidence_values(void* rec_void)
178{
179  int i, num_fixed = 0;
180  srec* rec = (srec*)rec_void;
181  AstarStack* stack = rec ? rec->astar_stack : 0;
182  if (!stack)
183    return num_fixed;
184  for(i=1; i<stack->num_complete_paths; i++) {
185    partial_path* parp = stack->complete_paths[i];
186    for (; parp; parp = parp->next) {
187      word_token* wtoken = &rec->word_token_array[ parp->token_index];
188      if(WORD_TOKEN_GET_HOMONYM( wtoken)) {
189        stack->complete_path_confidences[i] = stack->complete_path_confidences[i-1];
190        num_fixed++;
191        break;
192      }
193    }
194  }
195  return num_fixed;
196}
197
198LCHAR* srec_nbest_get_word(void* nbest, size_t choice)
199{
200  srec* rec = (srec*)nbest;
201  return rec->context->olabels->words[choice];
202}
203
204int srec_nbest_remove_result(void* rec_void, int n)
205{
206  int i;
207  srec* rec = (srec*)rec_void;
208  AstarStack* stack = rec ? rec->astar_stack : 0;
209
210
211  if (!stack || n < 0 || n >= stack->num_complete_paths)
212  {
213    return 0; /* out of range error */
214  }
215
216  /* free the partial_path which represents the entry */
217  free_partial_path(stack, stack->complete_paths[n]);
218
219  /* now I need to move everybody up one so I do not have a hole
220     in the middle of my nbest list */
221  for (i = n + 1 ; i < stack->num_complete_paths; i++)
222    stack->complete_paths[i-1] = stack->complete_paths[i];
223  stack->complete_paths[i-1] = 0; /* empty the last one */
224
225  /* finally change the size of my nbest list */
226  stack->num_complete_paths--;
227
228  return 1;
229}
230
231ESR_ReturnCode srec_nbest_get_resultWordIDs(void* rec_void, size_t index, wordID* wordIDs, size_t* len, asr_int32_t* cost)
232{
233  const srec* rec = (srec*) rec_void;
234  AstarStack* stack = rec ? rec->astar_stack : 0;
235  partial_path* parp;
236  wordID id;
237  size_t currentLen = 0;
238
239  if (!stack || index >= (size_t) stack->num_complete_paths)
240  {
241    if (wordIDs) *wordIDs = MAXwordID;
242    if (len) *len = 0;
243    *cost = MAXbcostdata;
244    return ESR_ARGUMENT_OUT_OF_BOUNDS; /* out of range error */
245  }
246
247  parp = stack->complete_paths[index];
248  *cost = stack->complete_paths[index]->costsofar;
249  if (len == NULL || wordIDs == NULL)
250    return ESR_SUCCESS;
251  if (parp && parp->word == rec->context->beg_silence_word)
252    parp = parp->next;
253  while (parp)
254  {
255    id = parp->word;
256    if (id == rec->context->end_silence_word)
257      break;
258
259    if (currentLen >= *len)
260    {
261      *wordIDs = MAXwordID;
262      *len = currentLen + 1;
263      return ESR_BUFFER_OVERFLOW; /* too little space error */
264    }
265    *wordIDs = id;
266    ++wordIDs;
267    ++currentLen;
268    parp = parp->next;
269  }
270  --currentLen;
271
272  if (currentLen >= *len)
273  {
274    *wordIDs = MAXwordID;
275    *len = currentLen + 1;
276    return ESR_BUFFER_OVERFLOW; /* too little space error */
277  }
278  *wordIDs = MAXwordID;
279  *len = currentLen + 1;
280  return ESR_SUCCESS;
281}
282
283int srec_nbest_get_result(void* rec_void, int n, char* label, int label_len, asr_int32_t* cost, int whether_strip_slot_markers)
284{
285  const srec* rec = (srec*)rec_void;
286  AstarStack* stack = rec ? rec->astar_stack : 0;
287  partial_path* parp;
288  word_token* wtoken;
289  int return_len;
290
291  if (!stack || n < 0 || n >= stack->num_complete_paths)
292  {
293    *label = 0;
294    *cost = MAXbcostdata;
295    return 1; /* out of range error */
296  }
297
298  return_len = 0;
299  parp = stack->complete_paths[n];
300  *cost = stack->complete_paths[n]->costsofar;
301  for (; parp; parp = parp->next)
302  {
303    const char *p;
304    int lenp;
305    wtoken = &rec->word_token_array[ parp->token_index];
306    p = "NULL";
307    if (rec->context->olabels->words[wtoken->word])
308      p = rec->context->olabels->words[wtoken->word];
309    if (!strcmp(p, "-pau2-"))
310      break;
311
312    lenp = (char)strlen(p);
313    if (return_len + lenp >= label_len)
314    {
315      *label = 0;
316      return 1; /* too little space error */
317    }
318    strcpy(label + return_len, p);
319    return_len += lenp;
320    if (whether_strip_slot_markers)
321    {
322      if (label[return_len-2] == IMPORTED_RULES_DELIM)
323      {
324        label[return_len-2] = 0;
325        return_len -= 2;
326      }
327    }
328
329#define SHOW_END_TIMES 1
330#if SHOW_END_TIMES
331    {
332      char et[16];
333      lenp = sprintf(et, "@%d", wtoken->end_time);
334      if (return_len + lenp >= label_len)
335        return 0;
336      strcpy(label + return_len, et);
337      return_len += lenp;
338    }
339#endif
340    lenp = 1;
341    if (return_len + lenp >= label_len)
342      return 0; /* too little space error */
343    strcpy(label + return_len, " ");
344    return_len += lenp;
345  }
346  *(label + return_len) = 0;
347  return 0;
348}
349
350int srec_nbest_get_choice_info(void* rec_void, int ibest, asr_int32_t* infoval, char* infoname)
351{
352  srec* rec = (srec*)rec_void;
353  AstarStack* stack = rec ? rec->astar_stack : 0;
354
355  if (!stack)
356    return 1;
357
358  if (ibest < 0 || ibest >= stack->num_complete_paths)
359    return 1;
360
361  /*!strcmp(infoname,"num_speech_frames")||
362    !strcmp(infoname,"speech_frames_cost"))*/
363  if (1)
364  {
365    partial_path* parp = stack->complete_paths[ibest];
366    frameID start_frame = MAXframeID;
367    frameID i, end_frame = MAXframeID;
368    frameID num_speech_frames;
369    bigcostdata speech_frames_cost, start_cost = 0, end_cost = 0;
370    word_token* wtoken;
371    frameID num_words;
372
373    for (num_words = 0 ; parp; parp = parp->next)
374    {
375      if (parp->token_index == MAXwtokenID) break;
376      wtoken = &rec->word_token_array[ parp->token_index];
377      if (wtoken->word == rec->context->beg_silence_word)
378      {
379        start_frame = wtoken->end_time;
380        start_cost = wtoken->cost + rec->accumulated_cost_offset[ start_frame];
381        num_words--;
382      }
383      else if (parp->next &&
384               parp->next->token_index != MAXwtokenID &&
385               rec->word_token_array[ parp->next->token_index].word == rec->context->end_silence_word)
386      {
387        end_frame = wtoken->end_time;
388        end_cost = wtoken->cost + rec->accumulated_cost_offset[ end_frame];
389        num_words--;
390      }
391      num_words++;
392    }
393
394    if (start_frame != MAXframeID && end_frame != MAXframeID)
395    {
396      num_speech_frames = (frameID)(end_frame - start_frame);
397      speech_frames_cost = end_cost - start_cost;
398#define WTW_AT_NNREJ_TRAINING 40
399      speech_frames_cost = speech_frames_cost - (num_words + 1) * (rec->context->wtw_average - WTW_AT_NNREJ_TRAINING);
400      if (!strcmp(infoname,  "num_speech_frames"))
401        *infoval = num_speech_frames;
402      else if (!strcmp(infoname, "speech_frames_cost"))
403        *infoval = speech_frames_cost;
404      else if (!strcmp(infoname, "gsm_states_score_diff"))
405      {
406        /* this is the best cost, unconstrained by state sequence */
407        bigcostdata gsm_states_cost = 0;
408        for (i = start_frame + 1; i <= end_frame; i++)
409        {
410          gsm_states_cost += rec->cost_offset_for_frame[i];
411          *infoval = (asr_int32_t)speech_frames_cost - (asr_int32_t)gsm_states_cost;
412        }
413      }
414      else if (!strcmp(infoname, "gsm_words_score_diff"))
415      {
416        /* this is the best cost, unconstrained by word sequence */
417        /* we can do this with astar.c ... with some work */
418        *infoval = 0;
419      }
420      else if (!strcmp(infoname, "num_words"))
421      {
422        *infoval = num_words;
423      }
424      else if (!strcmp(infoname, "gsm_cost"))
425      {
426        bigcostdata gsm_states_cost = 0;
427        for (i = start_frame + 1; i <= end_frame; i++)
428          gsm_states_cost += rec->best_model_cost_for_frame[i];
429        *infoval = gsm_states_cost;
430      }
431      else if (!strcmp(infoname, "num_total_frames"))
432      {
433        *infoval = rec->current_search_frame;
434      }
435      else if (!strcmp(infoname, "gsm_cost_all_frames"))
436      {
437        bigcostdata gsm_states_cost = 0;
438        for (i = 0; i < rec->current_search_frame; i++)
439          gsm_states_cost += rec->best_model_cost_for_frame[i];
440        *infoval = gsm_states_cost;
441      }
442      else if (!strcmp(infoname, "acoustic_model_index"))
443      {
444        *infoval = rec->id;
445      }
446      else
447      {
448        log_report("Error: srec_nbest_get_choice_info does not know re %s\n", infoname);
449        return 1;
450      }
451    }
452  }
453  return 0;
454}
455
456
457int srec_nbest_sort(void* rec_void)
458{
459  srec* rec = (srec*)rec_void;
460  AstarStack* stack = rec ? rec->astar_stack : 0;
461  size_t i, j, n;
462  partial_path* parp;
463
464  if (!stack || stack->num_complete_paths < 1)
465    return 0; /* out of range error */
466
467  n = stack->num_complete_paths;
468
469  /* bubble sort is fine */
470  /* PLogError("** srec_nbest_sort **\n"); */
471  for (i = 0;i < n;i++)
472    for (j = i + 1;j < n;j++)
473      if (stack->complete_paths[j]->costsofar < stack->complete_paths[i]->costsofar)
474      {
475        /* PLogMessage(" %d %d", stack->complete_paths[j]->costsofar,      stack->complete_paths[j]->costsofar); */
476        parp = stack->complete_paths[i];
477        stack->complete_paths[i] = stack->complete_paths[j];
478        stack->complete_paths[j] = parp;
479      }
480  return 1;
481
482}
483