/*---------------------------------------------------------------------------*
* run_seq_lts.c *
* *
* Copyright 2007, 2008 Nuance Communciations, Inc. *
* *
* Licensed under the Apache License, Version 2.0 (the 'License'); *
* you may not use this file except in compliance with the License. *
* *
* You may obtain a copy of the License at *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an 'AS IS' BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* *
*---------------------------------------------------------------------------*/
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
#ifndef NO_STDERR
#include <stdio.h>
#else
extern void PrintError(char *msg, unsigned long p1, unsigned long p2, unsigned long p3);
#endif
#include "passert.h"
#include "pmemory.h"
#include "plog.h"
#include "phashtable.h"
#include "lts_error.h"
#include "lts.h"
#include "lts_seq_internal.h"
#include "port_fileio.h"
#include "platform_utils.h" /* strdup, safe_strtok, etc */
#define ASSERT(x) passert(x)
#ifdef TI_DSP
#include "tidsp_defines.h"
#endif
#ifdef _DEBUG
#define PRINT_LOAD_TREE_SUMMARY 0
#define PRINT_LOAD_TREE 0
#define PRINT_CONS_COMB 0
#define PRINT_DP_LETTER 0
#define PRINT_LTS_WORD 0
#define PRINT_DICT_LOOKUP 0
#endif
#define LTS_MARKER_WORD_START "WS"
#define LTS_MARKER_PRON_START "PS"
#define LTS_MARKER_SYLL_START "SS"
#define LTS_MARKER_SYLL_START_DD "SS%d"
#define LTS_MARKER_PIPESEP "|"
#define LTS_MARKER_PIPESEP_CHAR '|'
static int load_int(PORT_FILE *fp);
static SWIsltsResult load_lquestions(LQUESTION ***pquestions, int *pnum_questions, PORT_FILE *fp);
static SWIsltsResult free_lquestions(LQUESTION ** questions, int num_questions);
static SWIsltsResult load_letter_mapping(PORT_FILE *fp, LM **ppLetterMap);
static SWIsltsResult free_letter_mapping(LM *lm);
static SWIsltsResult load_phone_mapping(PORT_FILE *fp, PM **ppPhoneMap);
static SWIsltsResult free_phone_mapping(PM *pm);
static SWIsltsResult load_outputs(char ***poutputs, char ***pinputs, int *pnum, PORT_FILE *fp);
static SWIsltsResult free_outputs(char **outputs, char **inputs, int num);
static SWIsltsResult load_trees(RT_LTREE ***ptrees, int *num_letters,
LQUESTION ***pquestions, int *num_questions, LM **plm, PORT_FILE *fp);
static SWIsltsResult free_trees(RT_LTREE **trees, int num_letters, LQUESTION **questions, int num_questions, LM *lm);
static SWIsltsResult load_allowable_cons_comb(LTS *lts, PORT_FILE *fp);
static SWIsltsResult free_allowable_cons_comb(LTS *lts);
static SWIsltsResult load_question_strings(LTS* lts, PORT_FILE* fp);
static SWIsltsResult free_question_strings(LTS* lts);
#define find_letter_index( myLet, myLM) (myLM->letter_index_for_letter[ toupper(myLet)])
int find_phone(const char *ph, PM *pm);
int find_best_string(const char *str, LTS* lts);
int find_best_prefix_string(const char *str, LTS* lts);
int fill_up_dp_for_letter(LTS *lts, const char *input_word, int word_len, int index, int root_start, int root_end, int left_phone);
#define in_list(myV, myQ) (bitarray_read_bit( myQ->membership, myV))
#define qmatches(myQ, myU) (in_list( myU->properties[ myQ->type], myQ))
int matches(LQUESTION *q1, LQUESTION *q2, int type, LDP *dp) ;
int find_output_for_dp(LTS *lts, int *pbackoff_output);
int add_output(char *output, char **output_phone_string, int out_len, int max_phone_length);
int is_allowable_cons_comb(LTS *lts, const char *cons_string);
void adjust_syllable_boundaries(LTS *lts, char **output_phone_string, int num_out, int max_phone_length);
SWIsltsResult lts_for_word(LTS *lts, char *word, int word_len, char **output_phone_string, int max_phone_length, int *num_out);
/*------------
*
* bitarray
*
*-----------*/
#define bitarray_read_bit( biTs, iBiT) ( biTs[iBiT/16] & (1<<((iBiT)%16)) )
/* int bitarray_read_bit( unsigned short* bits, int iBit)
{ // ASSERT( iBit<256);
return bits[iBit/16] & (1<<((iBit)%16));
} */
void bitarray_write_bit( unsigned short* bits, int iBit, int iVal)
{
unsigned short sect;
ASSERT( iBit<256);
sect = bits[iBit/16];
if(iVal) { sect |= (1<<(iBit%16)); }
else { sect &= ~(1<<(iBit%16)); }
bits[ iBit/16] = sect;
}
void bitarray_populate_from_list(unsigned short* bits, char* list, int listlen)
{
unsigned int i;
for(i=0; i<UCHAR_MAX/sizeof(unsigned short)/8; i++)
bits[i] = 0;
for(i=0; i<(unsigned int)listlen; i++)
bitarray_write_bit( bits, list[i], 1);
}
/*-----------
*
* PHashTable
*
*-----------*/
static int HashCmpWord(const LCHAR *key1, const LCHAR *key2)
{ return strcmp((const char*)key1,(const char*)key2); }
static unsigned int HashGetCode(const void *key)
{
const char* k = (const char*)key;
unsigned int i, len, h = 0;
len = strlen(k);
for (i=0; i<len; i++) h = 31*h + (unsigned int)k[i];
return h;
}
void* my_PHashTableCreate_FromStrings( const char* strings[], int num_strings,
const LCHAR* hashName)
{
PHashTable* table = NULL;
ESR_ReturnCode rc = ESR_SUCCESS;
PHashTableArgs hashArgs;
int i;
hashArgs.capacity = 63;
hashArgs.compFunction = HashCmpWord; // PHASH_TABLE_DEFAULT_COMP_FUNCTION;
hashArgs.hashFunction = HashGetCode; // PHASH_TABLE_DEFAULT_HASH_FUNCTION;
hashArgs.maxLoadFactor = PHASH_TABLE_DEFAULT_MAX_LOAD_FACTOR;
rc = PHashTableCreate( &hashArgs, hashName, &table);
for(i=0; i<num_strings; i++) {
void* old;
/* formerly the code used linear lookup, so let's avoid dups to match up */
rc = PHashTableGetValue( table, strings[i], (void**)&old);
if(rc != ESR_SUCCESS) {
rc = PHashTablePutValue( table, strings[i], (const void *)i, NULL );
}
}
return table;
}
/*---------
*
* i/o
*
*---------*/
static int load_int(PORT_FILE *fp)
{
int v;
PORT_FREAD_INT16((uint16 *)&v, sizeof(int), 1, fp);
return v;
}
static SWIsltsResult load_lquestions(LQUESTION ***pquestions, int *pnum_questions, PORT_FILE *fp)
{
int i, num_questions;
LQUESTION ** questions;
SWIsltsResult nRes = SWIsltsSuccess;
num_questions = load_int(fp);
#if PRINT_LOAD_TREE_SUMMARY
pfprintf(PSTDOUT,"loading %d questions\n", num_questions);
#endif
*pquestions = questions = (LQUESTION**) lts_alloc(num_questions, sizeof(LQUESTION*));
if (questions == NULL) {
nRes = SWIsltsErrAllocResource;
goto CLEAN_UP;
}
for (i=0;i<num_questions;i++) {
questions[i] = (LQUESTION*) lts_alloc(1, sizeof(LQUESTION));
if (questions[i] == NULL) {
nRes = SWIsltsErrAllocResource;
goto CLEAN_UP;
}
#if PRINT_LOAD_TREE
pfprintf(PSTDOUT,"LOAD_TREE: loading question %d\n", i);
#endif
PORT_FREAD_CHAR(&(questions[i]->type), sizeof(char), 1, fp);
PORT_FREAD_CHAR(&(questions[i]->num_list), sizeof(char), 1, fp);
questions[i]->list = (unsigned char*) lts_alloc(questions[i]->num_list, sizeof(unsigned char));
if (questions[i]->list == NULL) {
nRes = SWIsltsErrAllocResource;
goto CLEAN_UP;
}
PORT_FREAD_CHAR(questions[i]->list, sizeof(char), (questions[i]->num_list), fp);
bitarray_populate_from_list( questions[i]->membership, (char*) questions[i]->list, questions[i]->num_list);
}
*pnum_questions = num_questions;
return SWIsltsSuccess;
CLEAN_UP:
free_lquestions(questions, num_questions);
*pnum_questions = 0;
*pquestions = NULL;
return nRes;
}
/* deallocate questions */
static SWIsltsResult free_lquestions(LQUESTION ** questions, int num_questions)
{
SWIsltsResult nRes = SWIsltsSuccess;
int i;
if (questions) {
for (i=0; i<num_questions; i++) {
if (questions[i]->list) {
FREE(questions[i]->list);
questions[i]->list = NULL;
}
FREE(questions[i]);
questions[i] = NULL;
}
FREE(questions);
}
return nRes;
}
static SWIsltsResult load_letter_mapping(PORT_FILE *fp, LM **ppLetterMap)
{
SWIsltsResult nRes = SWIsltsSuccess;
unsigned char len;
LM * lm;
int i;
/* pfprintf(PSTDOUT,"got len %d\n", len);*/
lm = (LM*) lts_alloc(1, sizeof(LM));
if (lm == NULL) {
nRes = SWIsltsErrAllocResource;
goto CLEAN_UP;
}
PORT_FREAD_CHAR(&len, sizeof(char), 1, fp);
lm->num_letters = len;
lm->letters = (char*) lts_alloc(len, sizeof(char));
if (lm->letters == NULL) {
nRes = SWIsltsErrAllocResource;
goto CLEAN_UP;
}
lm->type = (char*) lts_alloc(len, sizeof(char));
if (lm->type == NULL) {
nRes = SWIsltsErrAllocResource;
goto CLEAN_UP;
}
PORT_FREAD_CHAR(lm->letters, sizeof(char), len, fp);
PORT_FREAD_CHAR(lm->type, sizeof(char), len, fp);
{
unsigned int letter;
for (letter=0; letter <= UCHAR_MAX; letter++)
lm->letter_index_for_letter[letter] = LTS_MAXCHAR;
}
for (i=0;i<len;i++) {
char letter = toupper(lm->letters[i]);
lm->letters[i] = letter;
lm->letter_index_for_letter[(unsigned char)letter] = i;
}
*ppLetterMap = lm;
return SWIsltsSuccess;
CLEAN_UP:
free_letter_mapping(lm);
*ppLetterMap = NULL;
return nRes;
}
/* deallocate letter mapping */
static SWIsltsResult free_letter_mapping(LM *lm)
{
SWIsltsResult nRes = SWIsltsSuccess;
if (lm) {
if (lm->letters) {
FREE(lm->letters);
lm->letters = NULL;
}
if (lm->type) {
FREE(lm->type);
lm->type = NULL;
}
lm->num_letters = 0;
FREE(lm);
}
return nRes;
}
static SWIsltsResult load_phone_mapping(PORT_FILE *fp, PM **ppPhoneMap)
{
SWIsltsResult nRes = SWIsltsSuccess;
PM * pm;
int i;
unsigned char len;
char * ph;
pm = (PM*) lts_alloc(1, sizeof(PM));
if (pm == NULL) {
nRes = SWIsltsErrAllocResource;
goto CLEAN_UP;
}
pm->num_phones = load_int(fp);
pm->phones = (char**) lts_alloc(pm->num_phones, sizeof(char*));
if (pm->phones == NULL) {
nRes = SWIsltsErrAllocResource;
goto CLEAN_UP;
}
for (i=0;i<pm->num_phones;i++) {
PORT_FREAD_CHAR(&len, sizeof(unsigned char), 1, fp);
pm->phoneH = NULL;
pm->phones[i] = ph = (char*) lts_alloc(len+1, sizeof(char));
if (ph == NULL) {
nRes = SWIsltsErrAllocResource;
goto CLEAN_UP;
}
PORT_FREAD_CHAR(ph, sizeof(char), len, fp);
ph[len] = '\0';
}
pm->phoneH = my_PHashTableCreate_FromStrings( (const char**)pm->phones,
pm->num_phones,
L("lts.phoneH"));
if(pm->phoneH == NULL) {
nRes = SWIsltsErrAllocResource;
goto CLEAN_UP;
}
*ppPhoneMap = pm;
return SWIsltsSuccess;
CLEAN_UP:
free_phone_mapping(pm);
*ppPhoneMap = NULL;
return nRes;
}
/* deallocate phone mapping */
static SWIsltsResult free_phone_mapping(PM *pm)
{
SWIsltsResult nRes = SWIsltsSuccess;
int i;
if (pm) {
if (pm->phones) {
for (i=0; i<pm->num_phones; i++) {
if (pm->phones[i]) {
FREE(pm->phones[i]);
pm->phones[i] = NULL;
}
}
FREE(pm->phones);
pm->phones = NULL;
}
if(pm->phoneH)
PHashTableDestroy( (PHashTable*)pm->phoneH);
pm->phoneH = NULL;
FREE(pm);
}
return nRes;
}
static SWIsltsResult load_outputs(char ***poutputs, char ***pinputs, int *pnum, PORT_FILE *fp)
{
SWIsltsResult nRes = SWIsltsSuccess;
int i;
char ** outputs = NULL;
char ** inputs = NULL;
int num;
unsigned char olen;
char * out;
unsigned char ilen;
char * in;
num = load_int(fp);
*poutputs = outputs = (char **) lts_alloc(num, sizeof(char*));
if (outputs == NULL) {
nRes = SWIsltsErrAllocResource;
goto CLEAN_UP;
}
*pinputs = inputs = (char **) lts_alloc(num, sizeof(char*));
if (inputs == NULL) {
nRes = SWIsltsErrAllocResource;
goto CLEAN_UP;
}
for (i=0;i<num;i++) {
PORT_FREAD_CHAR(&olen, sizeof(char), 1, fp);
out = outputs[i] = lts_alloc(olen + 1, sizeof(char));
if (out == NULL) {
nRes = SWIsltsErrAllocResource;
goto CLEAN_UP;
}
if (olen > 0) {
PORT_FREAD_CHAR(out, sizeof(char), olen, fp);
}
out[olen] = '\0';
PORT_FREAD_CHAR(&ilen, sizeof(char), 1, fp);
in = inputs[i] = lts_alloc(ilen + 1, sizeof(char));
if (in == NULL) {
nRes = SWIsltsErrAllocResource;
goto CLEAN_UP;
}
if (ilen > 0) {
PORT_FREAD_CHAR(in, sizeof(char), ilen, fp);
}
in[ilen] = '\0';
#if PRINT_LOAD_TREE
if (ilen > 0) pfprintf(PSTDOUT,"LOAD_TREE: got input %s out %s\n", in, outputs[i]);
pfprintf(PSTDOUT,"LOAD_TREE: outputs[%d] len %d out %x out %s\n", i, olen, outputs[i], outputs[i]);
#endif
}
*pnum = num;
return SWIsltsSuccess;
CLEAN_UP:
free_outputs(outputs, inputs, num);
*poutputs = NULL;
*pinputs = NULL;
*pnum = 0;
return nRes;
}
static SWIsltsResult free_outputs(char **outputs, char **inputs, int num)
{
SWIsltsResult nRes = SWIsltsSuccess;
int i;
if (outputs) {
for (i=0; i<num; i++) {
if (outputs[i]) {
FREE(outputs[i]);
outputs[i] = NULL;
}
}
FREE(outputs);
}
if (inputs) {
for (i=0; i<num; i++) {
if (inputs[i]) {
FREE(inputs[i]);
inputs[i] = NULL;
}
}
FREE(inputs);
}
return nRes;
}
static SWIsltsResult load_trees(RT_LTREE ***ptrees, int *num_letters,
LQUESTION ***pquestions, int *num_questions, LM **plm, PORT_FILE *fp)
{
SWIsltsResult nRes = SWIsltsSuccess;
int let, i;
RT_LTREE * tree = NULL;
RT_LTREE ** trees = NULL;
#if PRINT_LOAD_TREE_SUMMARY
pfprintf(PSTDOUT,"loading letter mapping\n");
#endif
*ptrees = NULL;
*pquestions = NULL;
*plm = NULL;
nRes = load_letter_mapping(fp, plm);
if (nRes != SWIsltsSuccess) {
goto CLEAN_UP;
}
#if PRINT_LOAD_TREE_SUMMARY
pfprintf(PSTDOUT,"loading questions\n");
#endif
nRes = load_lquestions(pquestions, num_questions, fp);
if (nRes != SWIsltsSuccess) {
goto CLEAN_UP;
}
*num_letters = load_int(fp);
if (*num_letters != (*plm)->num_letters) {
#ifndef NO_STDERR
PLogError(L("Error loading data, num_letters %d doesn't match num from mapping %d\n"),
*num_letters, (*plm)->num_letters);
#endif
nRes = SWIsltsInternalErr;
goto CLEAN_UP;
}
*ptrees = trees = (RT_LTREE**) lts_alloc(*num_letters, sizeof(RT_LTREE*));
if (trees == NULL) {
nRes = SWIsltsErrAllocResource;
goto CLEAN_UP;
}
for (let=0;let<*num_letters;let++) {
/* pfprintf(PSTDOUT,"loading for t %d\n", t);*/
trees[let] = tree = (RT_LTREE*) lts_alloc(1, sizeof(RT_LTREE));
if (tree == NULL) {
nRes = SWIsltsErrAllocResource;
goto CLEAN_UP;
}
tree->num_nodes = load_int(fp);
tree->values_or_question1 = (short*) lts_alloc(tree->num_nodes, sizeof(short));
if (tree->values_or_question1 == NULL) {
nRes = SWIsltsErrAllocResource;
goto CLEAN_UP;
}
tree->question2 = (short*) lts_alloc(tree->num_nodes, sizeof(short));
if (tree->question2 == NULL) {
nRes = SWIsltsErrAllocResource;
goto CLEAN_UP;
}
tree->left_nodes = (short *) lts_alloc(tree->num_nodes, sizeof(short));
if (tree->left_nodes == NULL) {
nRes = SWIsltsErrAllocResource;
goto CLEAN_UP;
}
#if PRINT_LOAD_TREE
pfprintf(PSTDOUT,"LOAD_TREE: Tree for let %d num_nodes %d\n", let, tree->num_nodes);
#endif
for (i=0;i<tree->num_nodes;i++) {
PORT_FREAD_INT16(&(tree->left_nodes[i]), sizeof(short), 1, fp);
PORT_FREAD_INT16(&(tree->values_or_question1[i]), sizeof(short), 1, fp);
#if PRINT_LOAD_TREE
pfprintf(PSTDOUT,"LOAD_TREE: node[%d] %d %d", i, tree->left_nodes[i], tree->values_or_question1[i]);
#endif
PORT_FREAD_INT16(&(tree->question2[i]), sizeof(short), 1, fp);
if (tree->left_nodes[i] != NO_NODE) {
if (tree->question2[i] == -1) tree->question2[i] = 0;
#if PRINT_LOAD_TREE
pfprintf(PSTDOUT," %x", (unsigned short) tree->question2[i]);
#endif
}
#if PRINT_LOAD_TREE
pfprintf(PSTDOUT,"\n");
#endif
}
}
return SWIsltsSuccess;
CLEAN_UP:
free_trees(trees, *num_letters, *pquestions, *num_questions, *plm);
*ptrees = NULL;
*pquestions = NULL;
*plm = NULL;
*num_letters = 0;
*num_questions = 0;
return nRes;
}
/* deallocate trees */
static SWIsltsResult free_trees(RT_LTREE **trees, int num_letters,
LQUESTION **questions, int num_questions, LM *lm)
{
SWIsltsResult nRes = SWIsltsSuccess;
int i;
RT_LTREE * tree;
if (lm) {
free_letter_mapping(lm);
}
if (questions) {
free_lquestions(questions, num_questions);
}
if (trees) {
for (i=0; i<num_letters; i++) {
if (trees[i]) {
tree = trees[i];
if (tree->values_or_question1) {
FREE(tree->values_or_question1);
tree->values_or_question1 = NULL;
}
if (tree->question2) {
FREE(tree->question2);
tree->question2 = NULL;
}
if (tree->left_nodes) {
FREE(tree->left_nodes);
tree->left_nodes = NULL;
}
FREE(trees[i]);
trees[i] = NULL;
}
}
FREE(trees);
}
return nRes;
}
static SWIsltsResult load_allowable_cons_comb(LTS *lts, PORT_FILE *fp)
{
SWIsltsResult nRes = SWIsltsSuccess;
char line[50];
char tempstr[50];
char * tok;
int i, toklen;
int count;
char seps[] = " \n";
lts->num_cons_comb = 0;
lts->allowable_cons_combH = NULL;
while (PORT_FGETS(line, 50, fp)) {
#ifndef TI_DSP
/*need to get rid of sme crud at the end of the line because it is being read in binary mode*/
for (i=strlen(line)-1;i>=0;i--) {
if (!isalpha(line[i])) line[i] = ' ';
}
#endif
count = 0;
tok = safe_strtok(line, seps, &toklen);
tempstr[0] = '\0';
/* get all available sequence of tokens */
while(tok && toklen > 0){
count += toklen;
strncat(tempstr, tok, toklen);
tempstr[count+1] = '\0';
strcat(tempstr, " ");
count++;
tok = safe_strtok(tok+toklen, seps, &toklen);
}
if (count > 0) {
/* delete the final space */
tempstr[count-1] = '\0';
lts->allowable_cons_comb[lts->num_cons_comb] = (char*) lts_alloc(strlen(tempstr)+1, sizeof(char));
if (lts->allowable_cons_comb[lts->num_cons_comb] == NULL) {
nRes = SWIsltsErrAllocResource;
goto CLEAN_UP;
}
strcpy(lts->allowable_cons_comb[lts->num_cons_comb], tempstr);
#if PRINT_CONS_COMB
pfprintf(PSTDOUT,"LOAD_TREE: allowable_cons_comb[%d]: %s\n", lts->num_cons_comb, tempstr);
#endif
lts->num_cons_comb++;
if (lts->num_cons_comb >= MAX_CONS_COMB) {
#ifndef NO_STDERR
PLogError(L("MAX_CONS_COMB %d exceeded\n"), MAX_CONS_COMB);
#endif
nRes = SWIsltsInternalErr;
goto CLEAN_UP;
}
}
}
if (lts->num_cons_comb == 0) {
#ifndef NO_STDERR
PLogError(L("Warning: the data file is missing consonant combinations - syllable boundaries will be incorrect\n"));
#endif
}
lts->allowable_cons_combH = my_PHashTableCreate_FromStrings( (const char**)lts->allowable_cons_comb, lts->num_cons_comb, L("lts.allowable_cons_combH"));
if(lts->allowable_cons_combH == NULL) {
nRes = SWIsltsErrAllocResource;
goto CLEAN_UP;
}
#if PRINT_LOAD_TREE_SUMMARY
pfprintf(PSTDOUT,"loaded %d cons combinations\n", lts->num_cons_comb);
#endif
return SWIsltsSuccess;
CLEAN_UP:
free_allowable_cons_comb(lts);
return nRes;
}
static SWIsltsResult free_allowable_cons_comb(LTS *lts)
{
SWIsltsResult nRes = SWIsltsSuccess;
int i;
for (i=0; i<lts->num_cons_comb; i++) {
if (lts->allowable_cons_comb[i]) {
FREE(lts->allowable_cons_comb[i]);
lts->allowable_cons_comb[i] = NULL;
}
}
if(lts->allowable_cons_combH)
PHashTableDestroy( (PHashTable*)lts->allowable_cons_combH);
lts->allowable_cons_combH = NULL;
return nRes;
}
static SWIsltsResult load_question_strings(LTS* lts, PORT_FILE* fp)
{
SWIsltsResult nRes = SWIsltsSuccess;
int i;
int num;
unsigned char len;
char ** strings;
char * str;
num = load_int(fp);
lts->strings = strings = (char **) lts_alloc(num, sizeof(char*));
lts->string_lens = (char*)lts_alloc(num, sizeof(char));
if (strings == NULL || lts->string_lens == NULL ) {
nRes = SWIsltsErrAllocResource;
goto CLEAN_UP;
}
for (i=0;i<num;i++) {
PORT_FREAD_CHAR(&len, sizeof(char), 1, fp);
str = strings[i] = lts_alloc(len + 1, sizeof(char));
if (str == NULL) {
nRes = SWIsltsErrAllocResource;
goto CLEAN_UP;
}
if (len > 0) {
PORT_FREAD_CHAR(str, sizeof(char), len, fp);
}
str[len] = '\0';
bitarray_populate_from_list( lts->membership, lts->strings[i], len);
lts->string_lens[i] = strlen(lts->strings[i]);
}
// *pnum = num;
lts->num_strings = num;
return SWIsltsSuccess;
CLEAN_UP:
free_question_strings(lts);
return nRes;
}
/* deallocate question strings */
static SWIsltsResult free_question_strings(LTS* lts)
{
SWIsltsResult nRes = SWIsltsSuccess;
int i;
if (lts->strings) {
for (i=0;i<lts->num_strings;i++) {
if (lts->strings[i]) {
FREE(lts->strings[i]);
lts->strings[i] = NULL;
}
}
FREE(lts->strings);
if(lts->string_lens) FREE(lts->string_lens);
lts->strings = NULL;
lts->string_lens = NULL;
}
return nRes;
}
SWIsltsResult create_lts(char *data_filename, LTS_HANDLE *phLts)
{
SWIsltsResult nRes = SWIsltsSuccess;
LTS * lts;
#ifdef USE_STATIC_SLTS
/* TODO: language-specific ID here? */
lts = &g_lts;
#else /* !USE_STATIC_SLTS */
PORT_FILE *fp;
lts = (LTS*) lts_alloc(1, sizeof(LTS));
if (lts == NULL) {
nRes = SWIsltsErrAllocResource;
goto CLEAN_UP;
}
fp = PORT_FOPEN(data_filename, "rb");
if (fp == NULL) {
#ifndef NO_STDERR
PLogError(L("Cannot open %s\n"), data_filename);
#endif
nRes = SWIsltsFileOpenErr;
goto CLEAN_UP;
}
nRes = load_phone_mapping(fp, <s->phone_mapping);
if (nRes != SWIsltsSuccess) {
PLogError(L("SWIsltsErr: load_phone_mapping() failed: Err_code = %d\n"), nRes);
goto CLEAN_UP;
}
nRes = load_question_strings(lts, fp);
if (nRes != SWIsltsSuccess) {
PLogError(L("SWIsltsErr: load_question_strings() failed: Err_code = %d\n"), nRes);
goto CLEAN_UP;
}
nRes = load_outputs(&(lts->outputs), &(lts->input_for_output), <s->num_outputs, fp);
if (nRes != SWIsltsSuccess) {
PLogError(L("SWIsltsErr: load_outputs() failed: Err_code = %d\n"), nRes);
goto CLEAN_UP;
}
#if PRINT_LOAD_TREE
pfprintf(PSTDOUT,"LOAD_TREE: got %d outputs, loading trees\n", lts->num_outputs);
#endif
nRes = load_trees(&(lts->trees), &(lts->num_letters),
&(lts->questions), &(lts->num_questions),
&(lts->letter_mapping),
fp);
if (nRes != SWIsltsSuccess) {
PLogError(L("SWIsltsErr: load_trees() failed: Err_code = %d\n"), nRes);
goto CLEAN_UP;
}
nRes = load_allowable_cons_comb(lts, fp);
if (nRes != SWIsltsSuccess) {
PLogError(L("SWIsltsErr: load_allowable_cons_comb() failed: Err_code = %d\n"), nRes);
goto CLEAN_UP;
}
PORT_FCLOSE(fp);
#endif /* !USE_STATIC_SLTS */
*phLts = lts;
return SWIsltsSuccess;
CLEAN_UP:
free_lts(lts);
*phLts = NULL;
return nRes;
}
/* deallocates LTS */
SWIsltsResult free_lts(LTS_HANDLE hlts)
{
SWIsltsResult nRes = SWIsltsSuccess;
LTS * lts = (LTS *)hlts;
if (lts) {
#ifndef USE_STATIC_SLTS
free_phone_mapping(lts->phone_mapping);
free_question_strings(lts);
lts->strings = NULL;
lts->phone_mapping = NULL;
free_outputs(lts->outputs, lts->input_for_output, lts->num_outputs);
lts->input_for_output = lts->outputs = NULL;
free_trees(lts->trees, lts->num_letters,
lts->questions, lts->num_questions,
lts->letter_mapping);
lts->trees = NULL;
lts->questions = NULL;
lts->letter_mapping = NULL;
free_allowable_cons_comb(lts);
FREE(lts);
#endif /* !USE_STATIC_LTS */
}
return nRes;
}
int find_phone(const char *ph, PM *pm)
{
ESR_ReturnCode rc;
int iRet = -1;
rc = PHashTableGetValue((PHashTable*)pm->phoneH, ph, (void**)(void*)&iRet);
if (rc != ESR_SUCCESS)
PLogError("error while in find_phone(%s,%x)\n", ph, pm);
return iRet;
}
int find_best_string(const char *str, LTS* lts)
{
int i, maxlen, maxi, len;
int len_str;
if(str[0] == '\0') return -1;
len_str = strlen(str);
maxi = -1;
maxlen = 0;
for (i=0;i<lts->num_strings;i++) {
len = lts->string_lens[i];
if( len > len_str)
continue; /* no point in comparison */
if (strncmp(str, lts->strings[i], len) == 0) {
if (len > maxlen) {
maxlen = len;
maxi = i;
}
}
}
return maxi;
}
int find_best_prefix_string(const char *str, LTS* lts)
{
int i;
int maxlen;
int maxi;
int len;
int prelen;
maxi = -1;
maxlen = 0;
prelen = strlen(str);
for (i=0;i<lts->num_strings;i++) {
len = lts->string_lens[i];
if (len <= prelen) {
if (strncmp(str + (prelen - len), lts->strings[i], len) == 0) {
if (len > maxlen) {
maxlen = len;
maxi = i;
}
}
}
}
return maxi;
}
int fill_up_dp_for_letter(LTS *lts, const char *input_word, int word_len, int index, int root_start, int root_end, int left_phone)
{
int i,j;
LDP *dp;
unsigned char letter;
int hit_wb;
LM *lm;
unsigned char word[MAX_WORD_LEN];
char tempstr[MAX_WORD_LEN];
int first_syl_end;
int last_syl_start;
dp = &(lts->dp);
lm = lts->letter_mapping;
/* the LTS decision tree does not seem to be well trained at all for
the letter ' when followed by "s" ... It seems to result in the
phoneme 'm', which is wrong. "'t" seems to be OK though.
BAD: Kevin's : k6v6nmz ... pal's : palmz ... paul's : p{lz
BAD: janice's : jan6s6mz ... tom's house : t)mmz&h?s ... tonya's : t)ny6mz
BAD: jake's house : jAk6mz&h?s
Ignoring ' as below we get ...
BETTER: Kevin's : kev6nz ... pal's : palz ... paul's : p{lz
BETTER: janice's : jan6s6s ... tom's house : t)mz&h?s ... tonya's : t)ny6s
BETTER: jake's house : jAk6s&h?s
The proper solution requires a legitimate text normalizer with special
handling of cases like 's which would always put a "z" there,
except if preceded by an unvoiced stop (ptk) which requires a "s" there.
For now let's just skip the ' letter, which testing shows to be generally
safe (janice's, jake's etc are better but still not quite right). */
if(input_word[index] == '\'')
return 1; // same as unknown character
letter = find_letter_index(input_word[index], lm);
if (letter == LTS_MAXCHAR) {
/* lisa - we need to decide how to handle this case. Do we just silently skip unknown
characters or warn the app or user somehow*/
#ifdef NO_STDERR
PrintError("unknown character on input %c - skipping\n", input_word[index], NULL, NULL);
#else
PLogError(L("unknown character on input %c - skipping\n"), input_word[index]);
#endif
return 1;
}
hit_wb = 0;
/*pfprintf(PSTDOUT,"left context\n");*/
for (j=0;j<5;j++) {
if (hit_wb) {
dp->properties[ Left1+j] = find_letter_index(LTS_MARKER_PIPESEP_CHAR, lm);
} else {
i = index - (j+1);
if (i < 0) dp->properties[ Left1+j] = find_letter_index(LTS_MARKER_PIPESEP_CHAR, lm);
else {
dp->properties[ Left1+j] = find_letter_index(input_word[i], lm);
if (dp->properties[ Left1+j] == LTS_MAXCHAR) { /*assume an unknown character is a word boundary*/
dp->properties[ Left1+j] = find_letter_index(LTS_MARKER_PIPESEP_CHAR, lm);
hit_wb = 1;
}
}
}
}
/*pfprintf(PSTDOUT,"right context\n");*/
hit_wb = 0;
for (j=0;j<5;j++) {
if (hit_wb) {
dp->properties[ Right1+j] = find_letter_index(LTS_MARKER_PIPESEP_CHAR, lm);
} else {
i = index + (j+1);
if (i >= word_len) dp->properties[Right1+j] = find_letter_index(LTS_MARKER_PIPESEP_CHAR, lm);
else {
dp->properties[ Right1+j] = find_letter_index(input_word[i], lm);
if (dp->properties[ Right1+j] == LTS_MAXCHAR) { /*assume an unknown character is a word boundary*/
dp->properties[ Right1+j] = find_letter_index(LTS_MARKER_PIPESEP_CHAR, lm);
hit_wb = 1;
}
}
}
}
dp->letter = letter; // properties[ Letter] = letter;
dp->properties[ LeftPhone1] = left_phone;
/*pfprintf(PSTDOUT,"word stuff\n"); */
/*find word start and end - use unknown character as word boundaries*/
dp->properties[ WordLen] = word_len;
if (index == 0) dp->properties[ LetInWord] = 0;
else if (index == word_len-1) dp->properties[ LetInWord] = 2;
else dp->properties[ LetInWord] = 1;
for (i=0;i<word_len;i++) {
word[i] = find_letter_index(input_word[i], lm);
}
/*figure out syllable in word - not really syllables - just looks to see if is or at first or last vowel*/
/* pfprintf(PSTDOUT,"syl stuff\n");*/
first_syl_end = word_len;
for (i=0;i<word_len;i++) {
if (lm->type[word[i]] == 1) {
for (j=i+1;j<word_len;j++) {
if (lm->type[word[j]] != 1) break;
}
first_syl_end = j;
break;
}
}
last_syl_start = 0;
for (i=word_len-1;i>=0;i--) {
if (lm->type[word[i]] == 1) {
for (j=i-1;j>=0;j--) {
if (lm->type[word[j]] != 1) break;
}
last_syl_start = j;
break;
}
}
#if PRINT_DP_LETTER
pfprintf(PSTDOUT,"first_syl_end %d last_syl_start %d\n", first_syl_end, last_syl_start);
#endif
if (index > last_syl_start) dp->properties[ SylInWord] = 2;
else if (index < first_syl_end) dp->properties[ SylInWord] = 0;
else dp->properties[ SylInWord] = 1;
first_syl_end = word_len;
for (i=0;i<word_len;i++) {
if (lm->type[word[i]] == 1) {
for (j=i+1;j<word_len;j++) {
if (lm->type[word[j]] != 1) break;
}
for (;j<word_len;j++) {
if (lm->type[word[j]] == 1) break;
}
first_syl_end = j;
break;
}
}
last_syl_start = 0;
for (i=word_len-1;i>=0;i--) {
if (lm->type[word[i]] == 1) {
for (j=i-1;j>=0;j--) {
if (lm->type[word[j]] != 1) break;
}
for (;j>=0;j--) {
if (lm->type[word[j]] == 1) break;
}
last_syl_start = j;
break;
}
}
#if PRINT_DP_LETTER
pfprintf(PSTDOUT,"first_syl_end %d last_syl_start %d\n", first_syl_end, last_syl_start);
#endif
if (index > last_syl_start) dp->properties[ Syl2InWord] = 2;
else if (index < first_syl_end) dp->properties[ Syl2InWord] = 0;
else dp->properties[Syl2InWord] = 1;
first_syl_end = word_len;
for (i=root_start;i<root_end;i++) {
if (lm->type[word[i]] == 1) {
for (j=i+1;j<word_len;j++) {
if (lm->type[word[j]] != 1) break;
}
first_syl_end = j;
break;
}
}
last_syl_start = 0;
for (i=root_end-1;i>=root_start;i--) {
if (lm->type[word[i]] == 1) {
for (j=i-1;j>=0;j--) {
if (lm->type[word[j]] != 1) break;
}
last_syl_start = j;
break;
}
}
#if PRINT_DP_LETTER
pfprintf(PSTDOUT,"first_syl_end %d last_syl_start %d\n", first_syl_end, last_syl_start);
#endif
if (index > last_syl_start) dp->properties[SylInRoot] = 2;
else if (index < first_syl_end) dp->properties[ SylInRoot] = 0;
else dp->properties[ SylInRoot] = 1;
first_syl_end = word_len;
for (i=root_start;i<root_end;i++) {
if (lm->type[word[i]] == 1) {
for (j=i+1;j<word_len;j++) {
if (lm->type[word[j]] != 1) break;
}
for (;j<word_len;j++) {
if (lm->type[word[j]] == 1) break;
}
first_syl_end = j;
break;
}
}
last_syl_start = 0;
for (i=root_end-1;i>=root_start;i--) {
if (lm->type[word[i]] == 1) {
for (j=i-1;j>=0;j--) {
if (lm->type[word[j]] != 1) break;
}
for (;j>=0;j--) {
if (lm->type[word[j]] == 1) break;
}
last_syl_start = j;
break;
}
}
#if PRINT_DP_LETTER
pfprintf(PSTDOUT,"first_syl_end %d last_syl_start %d\n", first_syl_end, last_syl_start);
#endif
if (index > last_syl_start) dp->properties[Syl2InRoot] = 2;
else if (index < first_syl_end) dp->properties[Syl2InRoot] = 0;
else dp->properties[Syl2InRoot] = 1;
dp->properties[Left_DFRE] = index - root_start;
dp->properties[Right_DFRE] = (root_end - index) - 1;
/* pfprintf(PSTDOUT,"strings\n");*/
#if PRINT_DP_LETTER
pfprintf(PSTDOUT,"input word %s num_strings %d\n", input_word, lts->num_strings);
#endif
dp->properties[RightString] = find_best_string(input_word+index+1, lts);
strcpy(tempstr, input_word);
tempstr[index] = '\0';
dp->properties[LeftString] = find_best_prefix_string(tempstr, lts);
#if PRINT_DP_LETTER
pfprintf(PSTDOUT,"dp %c ", lm->letters[dp->letter]);
for (i=0;i<word_len;i++) {
pfprintf(PSTDOUT,"%c", lm->letters[word[i]]);
}
pfprintf(PSTDOUT," %c%c%c {%c} %c%c%c liw %d siw %d s2iw %d nw %d sir %d s2ir %d left_DFRE %d right_DFRE %d\n",
lm->letters[dp->left_context[2]],
lm->letters[dp->left_context[1]],
lm->letters[dp->left_context[0]],
lm->letters[dp->letter],
lm->letters[dp->right_context[0]],
lm->letters[dp->right_context[1]],
lm->letters[dp->right_context[2]],
dp->let_in_word,
dp->syl_in_word,
dp->syl2_in_word,
dp->word_len,
dp->syl_in_root,
dp->syl2_in_root,
dp->left_DFRE, dp->right_DFRE);
#endif
return 0;
}
int matches(LQUESTION *q1, LQUESTION *q2, int type, LDP *dp)
{
int m1, m2;
switch(type) {
case 0:
return qmatches(q1, dp);
case 1:
m1 = qmatches(q1, dp);
m2 = qmatches(q2, dp);
return(m1 && m2);
case 2:
m1 = qmatches(q1, dp);
m2 = qmatches(q2, dp);
return(m1 && !m2);
case 3:
m1 = qmatches(q1, dp);
m2 = qmatches(q2, dp);
return(!m1 && m2);
case 4:
m1 = qmatches(q1, dp);
m2 = qmatches(q2, dp);
return(!m1 && !m2);
default:
return -1;
}
/* should not come here */
return -1;
}
int find_output_for_dp(LTS *lts, int *pbackoff_output)
{
LDP *dp;
int index;
RT_LTREE *tree;
LQUESTION *q1;
LQUESTION *q2;
int comb_type;
int q2_index;
int left_index;
dp = &(lts->dp);
tree = lts->trees[dp->letter]; // properties[Letter]];
index = 0;
while (1) {
left_index = tree->left_nodes[index];
if (left_index == NO_NODE) { /*means its a leaf node*/
*pbackoff_output = tree->question2[index];
return tree->values_or_question1[index];
}
q1 = lts->questions[tree->values_or_question1[index]];
q2_index = tree->question2[index] & 0x1FFF;
comb_type = (tree->question2[index] & 0xE000) >> 13;
q2 = lts->questions[q2_index];
if (matches(q1, q2, comb_type, dp)) {
index = left_index;
} else {
index = left_index+1;
}
}
}
int add_output(char *output, char **output_phone_string, int out_len, int max_phone_length)
{
char *tok;
int toklen;
char seps[] = " ";
if (strlen(output) == 0) return out_len;
tok = safe_strtok(output, seps, &toklen);
while (tok && toklen) {
if ((toklen > 0) && (strncmp(tok, "null", 4) != 0)) {
if (isdigit(tok[toklen-1])) {
/*means it's a vowel. So, add a syllable boundary. It's position
gets adjusted later by adjust_syllable_boundaries()*/
strcpy(output_phone_string[out_len++], LTS_MARKER_SYLL_START);
if (out_len >= max_phone_length) return max_phone_length;
}
strncpy(output_phone_string[out_len], tok, toklen);
output_phone_string[out_len++][toklen] = '\0';
if (out_len >= max_phone_length) return max_phone_length;
}
tok = safe_strtok(tok+toklen, seps, &toklen);
}
return out_len;
}
int is_allowable_cons_comb(LTS *lts, const char *cons_string)
{
/* int i;
for (i=0;i<lts->num_cons_comb;i++) {
#if PRINT_CONS_COMB
pfprintf(PSTDOUT,"checking {%s} vs c[%d] {%s}\n", cons_string, i, lts->allowable_cons_comb[i]);
#endif
if (strcmp(cons_string, lts->allowable_cons_comb[i]) == 0) return 1;
}
return 0;
*/
ESR_ReturnCode rc;
void* iVal = NULL;
rc = PHashTableGetValue( (PHashTable*)lts->allowable_cons_combH, cons_string, &iVal);
if(rc == ESR_SUCCESS)
return 1;
else
return 0;
}
void adjust_syllable_boundaries(LTS *lts, char **output_phone_string, int num_out, int max_phone_length)
{
char *out;
int i,j;
int syl_start;
int stress = 0;
int first_syl_bound;
char tempstr[20];
/*there should already be a syllable boundary before each vowel (add_output put one there)*/
/*so just find these, then shift back by allowable consonant combinations and move the syllable mark*/
for (i=0;i<num_out;i++) {
out = output_phone_string[i];
if (strcmp(out, LTS_MARKER_SYLL_START) == 0) { /*means there is a syllable boundary
find start of allowable sequence*/
syl_start = 0;
for (j=i-1;j>0;j--) {
out = output_phone_string[j];
if (isdigit(out[strlen(out)-1])) {
syl_start = j+1;
break; /*means it's a vowel*/
}
if (strcmp(out, LTS_MARKER_WORD_START) == 0) {
syl_start = j+1;
break; /*don't push syl boundaries before word boundaries*/
}
if (strcmp(out, LTS_MARKER_PRON_START) == 0) {
syl_start = j+1;
break; /*don't push syl boundaries before phrase boundaries*/
}
/* for sequences longer than 2,
check 3-syllable onset first, then check 2-syllable onset */
if(j > 1){
sprintf(tempstr, "%s %s %s", output_phone_string[j-2], output_phone_string[j-1],
output_phone_string[j]);
if (!is_allowable_cons_comb(lts, tempstr)) {
sprintf(tempstr, "%s %s", output_phone_string[j-1], output_phone_string[j]);
if (!is_allowable_cons_comb(lts, tempstr)) {
#if PRINT_CONS_COMB
pfprintf(PSTDOUT,"cons comb %s %s not allowed\n", output_phone_string[j-1],
output_phone_string[j]);
#endif
syl_start = j;
break;
}
}
}
/* for sequences shorter than 2 */
else
{
sprintf(tempstr, "%s %s", output_phone_string[j-1], output_phone_string[j]);
if (!is_allowable_cons_comb(lts, tempstr)) {
#if PRINT_CONS_COMB
pfprintf(PSTDOUT,"cons comb %s %s not allowed\n", output_phone_string[j-1],
output_phone_string[j]);
#endif
syl_start = j;
break;
}
}
} /* end for j=i-1 */
/*shift over stuff between syl_start a gap*/
for (j=i;j>syl_start;j--) {
strcpy(output_phone_string[j], output_phone_string[j-1]);
}
/*now find stress level from phone (and remove it) and add it to syl bound*/
if (i<num_out-1) {
out = output_phone_string[i+1];
if (isdigit(out[strlen(out)-1])) {
stress = atoi(out + strlen(out)-1);
} else {
stress = 0; /*should not happen*/
}
} else {
stress = 0; /*should not happen*/
}
sprintf(output_phone_string[syl_start], LTS_MARKER_SYLL_START_DD, stress);
} /* end if (strcmp(out, LTS_MARKER_SYLL_START) == 0) */
} /* end for i=0 */
/*remove all the stress marking from the vowels*/
for (i=0;i<num_out;i++) {
out = output_phone_string[i];
if ((strncmp(out, LTS_MARKER_SYLL_START, 2) != 0) && isdigit(out[strlen(out)-1])) {
out[strlen(out)-1] = '\0'; /*remove the stress from the vowel*/
}
}
/* word boundary must be followed by syllable boundary
if no syllable boundary exists after a word boundary, move the first
syllable boundary to after the word boundary */
first_syl_bound = -1;
syl_start = -1;
for (i=1;i<num_out;i++) {
if ((strcmp(output_phone_string[i-1], LTS_MARKER_WORD_START) == 0) &&
(strncmp(output_phone_string[i], LTS_MARKER_SYLL_START, 2) != 0)) {
syl_start = i;
/* search for first occurance of syllable boundary */
for(j=syl_start+1;j<num_out; j++){
out = output_phone_string[j];
if(strncmp(out, LTS_MARKER_SYLL_START, 2) == 0 && isdigit(out[strlen(out)-1])){
stress = atoi(out + strlen(out)-1);
first_syl_bound = j;
break;
}
}
/* swap entries until syl bound reaches word bound */
if(first_syl_bound >= 0){
for(; j>syl_start; j--){
strcpy(output_phone_string[j], output_phone_string[j-1]);
}
/* put syllable boundary after word boundary */
sprintf(output_phone_string[syl_start], LTS_MARKER_SYLL_START_DD, stress);
/* advance i, reset variables */
i = first_syl_bound;
first_syl_bound = syl_start = -1;
}
}
}
}
SWIsltsResult lts_for_word(LTS *lts, char *word, int word_len, char **output_phone_string, int max_phone_length, int *pnum_out)
{
SWIsltsResult nRes = SWIsltsSuccess;
int i,j;
int root_start;
int root_end;
int output_index;
int left_phone;
char * input_seq;
int found_match;
int start_num_out;
int backoff_output;
int num_out;
start_num_out = num_out = *pnum_out;
root_start = 0;
root_end = word_len;
for (i=0;i<word_len;i++) {
if ((i == 0) || (num_out == 0)) {
/* pfprintf(PSTDOUT,"about to call find_phone1\n");*/
left_phone = find_phone(LTS_MARKER_PIPESEP, lts->phone_mapping);
#if PRINT_LTS_WORD
pfprintf(PSTDOUT,"got phone %d for initial | (LTS_MARKER_PIPESEP)\n", left_phone);
#endif
if (left_phone < 0) {
#ifdef NO_STDERR
PrintError("Error, cannot find | in phone mappings\n", NULL, NULL, NULL);
#else
PLogError(L("Error, cannot find | in phone mappings\n"));
#endif
nRes = SWIsltsInternalErr;
goto CLEAN_UP;
}
} else {
#if PRINT_LTS_WORD
pfprintf(PSTDOUT,"about to call find_phone2 num_out %d\n", num_out);
pfprintf(PSTDOUT,"out[%d] %s\n", num_out-1, output_phone_string[num_out-1]);
#endif
if (strcmp(output_phone_string[num_out-1], LTS_MARKER_PRON_START) == 0) left_phone = find_phone(LTS_MARKER_PIPESEP, lts->phone_mapping);
else if (strcmp(output_phone_string[num_out-1], LTS_MARKER_WORD_START) == 0) left_phone = find_phone(LTS_MARKER_PIPESEP, lts->phone_mapping);
else left_phone = find_phone(output_phone_string[num_out-1], lts->phone_mapping);
#if PRINT_LTS_WORD
pfprintf(PSTDOUT,"got phone %d for %s\n", left_phone, output_phone_string[num_out-1]);
#endif
if (left_phone < 0) {
#ifdef NO_STDERR
PrintError("Error, cannot find %s in phone mappings\n", (unsigned long)output_phone_string[num_out-1], NULL, NULL);
#else
PLogError(L("Error, cannot find %s in phone mappings\n"), output_phone_string[num_out-1]);
#endif
nRes = SWIsltsInternalErr;
goto CLEAN_UP;
}
}
/* pfprintf(PSTDOUT,"calling fill up dp\n");*/
if (fill_up_dp_for_letter(lts, word, word_len, i, root_start, root_end, left_phone)) continue;
/* pfprintf(PSTDOUT,"calling find output\n");*/
output_index = find_output_for_dp(lts, &backoff_output);
#if PRINT_LTS_WORD
pfprintf(PSTDOUT,"got output %d\n", output_index);
#endif
found_match = 1;
if (strlen(lts->input_for_output[output_index]) > 0) {
/*some extra input string to use up*/
#if PRINT_LTS_WORD
pfprintf(PSTDOUT,"GOT INPUT %s for %s letter %c\n", lts->input_for_output[output_index], word, word[i]);
#endif
input_seq = lts->input_for_output[output_index];
if (input_seq[0] == '=') {
root_end = i;
input_seq = input_seq+1; /*skip suffix indicator*/
}
for (j=i+1;;j++) {
if (input_seq[j-(i+1)] == '\0') break;
if (input_seq[j-(i+1)] == '-') {
root_start = j;
break;
}
if (j >= word_len) {
found_match = 0;
break;
}
if (input_seq[j-(i+1)] != word[j]) {
found_match = 0;
break;
}
}
if (found_match) {
i = j-1;
}
}
if (!found_match) {
#if PRINT_LTS_WORD
pfprintf(PSTDOUT,"using backoff output %s instead of regular %s\n",
lts->outputs[backoff_output],
ts->outputs[output_index]);
#endif
num_out = add_output(lts->outputs[backoff_output], output_phone_string, num_out, max_phone_length);
}
else {
num_out = add_output(lts->outputs[output_index], output_phone_string, num_out, max_phone_length);
}
if (num_out >= max_phone_length) {
nRes = SWIsltsMaxInputExceeded;
goto CLEAN_UP;
}
}
*pnum_out = num_out;
return SWIsltsSuccess;
CLEAN_UP:
*pnum_out = 0;
return nRes;
}
SWIsltsResult run_lts(LTS_HANDLE h, FSM_DICT_HANDLE hdict, char *input_sentence, char **output_phone_string, int *phone_length)
{
SWIsltsResult nRes = SWIsltsSuccess;
int i;
int len;
int num_out = 0;
LTS * lts;
int was_in_phrase;
char word[MAX_WORD_LEN];
int num_in_word;
int max_phone_length;
int pron_len;
max_phone_length = *phone_length;
len = strlen(input_sentence);
lts = (LTS*) h;
was_in_phrase = 0;
/*add a phrase start then word start at beginning*/
strcpy(output_phone_string[num_out++], LTS_MARKER_PRON_START);
if (num_out >= max_phone_length) {
nRes = SWIsltsMaxInputExceeded;
goto CLEAN_UP;
}
num_in_word = 0;
pron_len = 1; // for the first time through
for (i=0;i<=len;i++) {
#if PRINT_LTS_WORD
pfprintf(PSTDOUT,"WORKING on letter %d %c\n", i, input_sentence[i]);
#endif
/* Treat hyphen as word delimiter. Not quite right for German
hyphenated compounds, but still an improvement. */
if ((input_sentence[i] == ' ') || (input_sentence[i] == '-') || (input_sentence[i] == '\t') || (i == len)) {
if (num_in_word>0 ) {
strcpy(output_phone_string[num_out++], LTS_MARKER_WORD_START);
if (num_out >= max_phone_length) {
nRes = SWIsltsMaxInputExceeded;
goto CLEAN_UP;
}
word[num_in_word] = '\0';
if (1) {
#if PRINT_DICT_LOOKUP
pfprintf(PSTDOUT,"Did not find %s in dictionary\n", word);
#endif
pron_len = -num_out;
nRes = lts_for_word(lts, word, num_in_word, output_phone_string, max_phone_length, &num_out);
pron_len += num_out; // now pron_len is the number of phonemes/markers added
if(pron_len == 0)
num_out--; // to backspace on the LTS_MARKER_WORD_START !!
if (nRes != SWIsltsSuccess) {
goto CLEAN_UP;
}
}
num_in_word = 0;
}
}
else if ( (input_sentence[i] == '.')
|| (input_sentence[i] == ',')
|| (input_sentence[i] == '!')
|| (input_sentence[i] == '?')
|| (input_sentence[i] == '\n')) {
if (was_in_phrase) {
/*add a phrase boundary after lts is called*/
if (num_in_word > 0) {
strcpy(output_phone_string[num_out++], LTS_MARKER_WORD_START);
if (num_out >= max_phone_length) {
nRes = SWIsltsMaxInputExceeded;
goto CLEAN_UP;
}
word[num_in_word] = '\0';
if (1) {
nRes = lts_for_word(lts, word, num_in_word, output_phone_string, max_phone_length, &num_out);
if (nRes != SWIsltsSuccess) {
goto CLEAN_UP;
}
}
num_in_word = 0;
}
strcpy(output_phone_string[num_out++], LTS_MARKER_PRON_START);
if (num_out >= max_phone_length) {
nRes = SWIsltsMaxInputExceeded;
goto CLEAN_UP;
}
was_in_phrase = 0;
}
}
else {
if (num_in_word < MAX_WORD_LEN-1) {
word[num_in_word++] = toupper(input_sentence[i]);
was_in_phrase = 1;
}
}
}
/*adjust syllable boundaries*/
adjust_syllable_boundaries(lts, output_phone_string, num_out, max_phone_length);
*phone_length = num_out;
return SWIsltsSuccess;
CLEAN_UP:
*phone_length = 0;
return nRes;
}
#ifdef USE_STATIC_SLTS
void *lts_alloc(int num, int size)
{
#ifdef NO_STDERR
PrintError("USE_STATIC_SLTS: lts_alloc should not be called", NULL, NULL, NULL);
#else
PLogError(L("USE_STATIC_SLTS: lts_alloc should not be called"));
#endif
return NULL;
}
#else
void *lts_alloc(int num, int size)
{
void *p;
p = CALLOC(num, size, MTAG);
return p;
}
#endif /* USE_STATIC_SLTS */