// file: $isip/util/demo/isip_comm_server_speech_recognition/isip_comm_speech_recognition.cc
// version: $Id: isip_comm_speech_recognition.cc 10403 2006-01-27 19:53:24Z stanley $
//

// include files
//
#include "isip_comm_speech_recognition.h"
#include <time.h>

// method:  _GalSS_print_usage
//
// arguments:
//  int argc: (input) number of arguments
//  char ** argv: (input) arguments in the command line
//  
// return value: none
//
// This function prints out the usage message for this server.  It is
// called by Galaxy Communicator structure when users using -help
// option.
//
void _GalSS_print_usage(int argc, char **argv) {
  
  GalUtil_OAPrintOptions(argc, argv, oas, NULL);
  printf("\n");
  
  // return none
  //
  return;
}


// method:  _GalSS_init_server
//
// arguments:
//  Gal_Server *server_a: (input) Galaxy server pointer
//  int argc: (input) number of arguments
//  char ** argv: (input) arguments in the command line
//  
// return value: none
//
// this function initializes the server according the command line
// input, required by Galaxy Communicator structure
//
void *_GalSS_init_server(Gal_Server *server_a, int argc, char **argv) {
  
  // local variables
  //
  sys_id = NULL;
  
  // verification the commandline options are correct (to a first
  // approximation)
  //
  if (GalUtil_OACheckUsage(argc, argv, oas, NULL) == 0) {
    exit(1);
  }

  // object that holds the parsed data from the frontend parameter
  // file
  //
  char* frontend_params = NULL;
  char* params = NULL;
  
  // get the frontend parameter file which the user gave in
  // commandline
  //  
  GalUtil_OAExtract(argc, argv, oas, "-frontend", GAL_OA_STRING,
		    &frontend_params);
  
  // make sure the user gave the frontend parameter file
  //
  if (frontend_params == NULL) {
    fprintf(stderr,"Can't find frontend parameter files. Exit!\n"); 
    fflush(stderr);
    exit(1);
  }
  else {
    
    // declare filename objects
    //
    String f_params;
    Filename pfile;
    f_params.assign((byte*)frontend_params);
    pfile.assign(f_params);
    Filename temp_pfile;
    Filename temp_maxfile;
    Filename temp_meanfile;
    
    //--------------------------------------------------------------------
    //
    // detect multiple recipes:
    //  frontend allows each input file to be processed through
    //  multiple recipes.
    //
    //---------------------------------------------------------------------
    
    // determine if the recipe file is a file list
    //
    if (Sdb::isSdb(pfile)) {
      sdb_params_d.append(pfile, true);
    }
    else {
      sdb_params_d.append(pfile);
    }
    
    //------------------------------------------------------------------------
    //
    // configure a vector of front ends
    //
    //------------------------------------------------------------------------
    
    // fetch the number of front-ends and allocate space
    //
    long num_fe = sdb_params_d.length();
    
    String output;
    output.assign(L"total recipes ");
    output.concat(num_fe);
    Console::putNoWrap(output);
    
    vector_param_d.setLength(num_fe);
    vector_fe_d.setLength(num_fe);
    
    // initialize Sdb processing of the parameter files
    //
    if (!sdb_params_d.gotoFirst()) {
      Error::handle(L"server", L"main", Sdb::ERR, __FILE__, __LINE__);
    }
    
    // copy the parameter files into temp files (this modification is
    // done in order to start multiple versions of demo at the same
    // time
    //
    Integral::makeTemp(temp_maxfile);
    Integral::makeTemp(temp_meanfile);
    
    SysString maxfile;
    SysString meanfile;
    meanfile.assign(L"mean_cepstrum.sof");
    maxfile.assign(L"max_energy.sof");
    
    // configure one front end at a time
    //
    for (long i = 0; i < num_fe; i++) {
      
      // get the next name
      //
      sdb_params_d.getName(pfile);
      
      // create the temporary parameter filename
      //
      Integral::makeTemp(temp_pfile);
      
      // get the next name
      //
      sdb_params_d.getName(pfile);
      
      // read the original recipe file and put it into a temporary
      // file with the required parameters changed
      //
      File tmp;
      tmp.open(pfile, File::READ_ONLY, File::TEXT);
      SysString str;
      String contents;
      while(!tmp.eof()) {
	tmp.get(str);
	contents.concat(str);
	contents.concat(L"\n");
      }
      
      // replace the max_energy.sof and mean_cepstrum.sof files with
      // temporary file names.
      //
      contents.replaceAll(maxfile, temp_maxfile);
      contents.replaceAll(meanfile, temp_meanfile);
      
      // open the temporary parameter file
      //
      File temp_param_file;
      String output;
      if (!temp_param_file.open(temp_pfile, File::APPEND_PLUS, File::TEXT)) {
	output.assign(L"\ncannot open file: ");
	output.concat(temp_pfile);
	output.concat(L", bailing out...");
	Console::put(output);
	Integral::exit();
      }
      
      temp_param_file.put(contents);
      temp_param_file.close();
      tmp.close();
      pfile.assign(temp_pfile);
      vector_param_d(i).assign(pfile);

      // debug message
      //
      String msg(L"loading parameter file: ");
      msg.concat(pfile);
      Console::put(msg);

      // load the features
      //
      if (!vector_fe_d(i).load(pfile)) {
	String msg(L"unable to open parameter file (\n");
	msg.concat(pfile);
	msg.concat(L")");
	Console::put(msg);
	Error::handle(L"server", L"main",
		      Error::NO_PARAM_FILE, __FILE__, __LINE__);
      }
      
      // advance sdb
      //
      sdb_params_d.gotoNext();            
    }
    
  }
  
  // get the parameter file for decoder
  //
  GalUtil_OAExtract(argc, argv, oas, "-decoder", GAL_OA_STRING, &params);
  if (params == NULL) {
    fprintf(stderr,"Can't find decoder parameter files. Exit!\n"); 
    fflush(stderr);
    exit(1);
  }
  else {
    
    //************************load_models***************************
    // load the models. This was previously wrapped in a load_models
    // method. The version below is the unwrapped code.
    //
    
    // open parameter file
    //
    FILE* fp = (FILE*)NULL;
    fp = fopen(params, "r");
    
    if (fp == NULL) {
      
      fprintf(stderr,"unable to open parameter file \n"); 
      fflush(stderr);
      exit(1);
    }
    
    // read parameters
    //
    tp.read_params_cc(fp);
    
    // configure the decoder according to these parameter
    //
    tp.config_decoder_cc(ISIP_FALSE, ISIP_TRUE);
  }
  
  // get decoder key id
  //
  GalUtil_OAExtract(argc, argv, oas, "-sysid", GAL_OA_STRING, &sys_id);
  
  // if no value, assign default value
  //
  if (sys_id == NULL) {
    strcpy(sys_id_value, "si");
  }
  else {
    strcpy(sys_id_value, sys_id);
  }
    
  // return none
  //
  return (void *) NULL;
}


// method: reset
//
// arguments:
//
//  Gal_Frame  f: (input) Galaxy Frame
//  void *server_data): (input) server information
//  
// return value: Galaxy Frame
//

// this function resets the server
//
Gal_Frame reset(Gal_Frame frame_a, void *server_data_a) {
  
  server_state_d = WAIT_AUDIO_READY;
  
  // return none
  //
  return (Gal_Frame) NULL;
}

// method: reinitialize
//
// arguments:
//
//  Gal_Frame  f: (input) Galaxy Frame
//  void *server_data): (input) server information
//  
// return value: Galaxy Frame
//
// keys in GFrame:
//       key   :restart              value:  [server_name]
//
// this function reinitializes the server and returns a NULL Galaxy
// Frame by following the Galaxy convention
//
Gal_Frame reinitialize(Gal_Frame frame_a, void *server_data_a) {
  
  // local variables
  //
  Gal_Frame new_frame;
  
  // get the program name from incoming frame
  //
  char *program_name = Gal_GetString(frame_a, ":program");
  
  if (!program_name) {
    program_name = "main";
  }
    
  strcpy(server_name_d, SERVER_NAME);
  
  // make a new frame and set properties to it
  //
  new_frame = Gal_MakeFrame(program_name, GAL_CLAUSE);
  Gal_SetProp(new_frame, ":restart", Gal_StringObject(server_name_d));
  Gal_SetProp(new_frame, ":program", Gal_StringObject(program_name));
  
  // send the frame to hub
  //
  GalSS_EnvWriteFrame((GalSS_Environment *) server_data_a, new_frame, 0);
  
  // free the frame
  //
  Gal_FreeFrame(new_frame);
  
  // print debug message
  //
  GalUtil_PInfo1("**** Received the reinitialize message *****\n");
  
  // initialize some important global variable
  //
  utt_counter_d = 0;
  
  // Change from
  //server_state_d = GET_PROCESS_ID;
  // in order to by-pass the PID code since the process manager no longer
  // needs this information.
  //
  server_state_d = WAIT_AUDIO_READY;
  
  // return gracefully
  //
  return (Gal_Frame) NULL;
}


// method: receive_signal
//
// arguments:
//
//  Gal_Frame  frame: (input) Galaxy Frame
//  void *server_data): (input) server information
//  
// return value: Galaxy Frame
//
// keys in GFrame:
//       key   :hub_logdir              value:
//       key   :hub_log_prefix          value:  
//
// this function gets the frame from hub and call process signal to
// process it. Returns a NULL Galaxy Frame by following the Galaxy
// convention
//
Gal_Frame receive_signal(Gal_Frame frame_a, void *server_data_a) {
  
  // call the preocess_frame function
  //
  process_frame(frame_a, server_data_a);
  
  // return gracefully
  //
  return (Gal_Frame)NULL;
}

// method: send_signal
//
// arguments:
//
//  Gal_Frame  frame: (input) Galaxy Frame
//  void *server_data): (input) server information
//  
// return value: none
//
// this function sends frame to hub 
//
void send_signal(Gal_Frame frame_a, void *server_data_a) {
  
  // send the frame to hub
  //
  GalSS_EnvWriteFrame((GalSS_Environment *) server_data_a, frame_a, 0);
  Gal_FreeFrame(frame_a);
}


// method: process_frame
//
// arguments:
//
//  Gal_Frame  frame: (input) Galaxy Frame
//  void *server_data): (input) server information
//  
// return value: none
//
// this function process frame according to different states of the
// system.
//
void process_frame(Gal_Frame frame_a, void *server_data_a) {

  // this module calls different methods depending on the state of the
  // speech recognition server
  //
  if (server_state_d == GET_PROCESS_ID) {
    state_get_process_id(frame_a, server_data_a);
  }
  else if (server_state_d == WAIT_AUDIO_READY) {
    
    state_audio_ready(frame_a, server_data_a);
  }
  else if (server_state_d == DATA_PROCESSING) {
    state_process_data(frame_a, server_data_a);
  }
}


// method: state_get_process_id
//
// arguments:
//
//  Gal_Frame frame: (input) Galaxy Frame
//  void *server_data): (input) server information
//  
// return value: none
//
// this function process frame when the system in the process id state.
//
void state_get_process_id(Gal_Frame frame_a, void *server_data_a) {
  
  // local variables
  //
  Gal_Frame new_frame;
  
  // get the audio_ready signal
  //
  int process_id;

  // get the value of the process_id key
  //
  process_id = Gal_GetInt(frame_a, ":process_id");
  
  if (process_id) {
    
    // get program name from incoming frame
    //
    char *program_name = Gal_GetString(frame_a, ":program");
    
    if (!program_name) {
      program_name = "main";
    }
      
    // get process id of this server
    //
    int pid = getpid();
    
    // make a new frame and set properties to it
    //
    new_frame = Gal_MakeFrame(program_name, GAL_CLAUSE);
    Gal_SetProp(new_frame, ":server_name", Gal_StringObject(server_name_d));
    Gal_SetProp(new_frame, ":pid", Gal_IntObject(pid));
    Gal_SetProp(new_frame, ":program", Gal_StringObject(program_name));
    
    // send the new frame to hub
    //
    send_signal(new_frame, server_data_a);
    
    // free the memory for new frame
    //
    Gal_FreeFrame(new_frame);
    
    server_state_d = WAIT_AUDIO_READY;
  }
  else {
    
    // print debug info and exit the system
    //
    fprintf(stdout, "Process id query does not been received, exit\n");
    fflush(stdout);
    exit(1);
  }
  
}

// method: state_audio_ready
//
// arguments:
//
//  Gal_Frame  frame: (input) Galaxy Frame
//  void *server_data): (input) server information
//  
// return value: none
//
// keys in GFrame:
//       key   :hub_logdir              value:
//       key   :hub_log_prefix          value:  
//
// this function process the frame when the system in the audio ready
// state.
//
void state_audio_ready(Gal_Frame frame_a, void *server_data_a) {
  
  // define local variable
  //
  Gal_Frame frame = NULL;
  
  // get the audio_ready signal
  //
  int audio_ready;
  audio_ready = Gal_GetInt(frame_a, ":audio_ready");

  // if the value of audio_ready is 1
  //
  if (audio_ready) {
    
    int button_counter = Gal_GetInt(frame_a, ":button_press_counter"); 
    sample_rate_d = (float)(Gal_GetFloat(frame_a, ":sample_rate"));
    
    // checking for compatibility with the models
    //
    SofParser parser;
    Sof sof0;
    SysString tmp_file;
    tmp_file.assign(L"../isip_demo_data/speech_recognition/models/sampling_frequency.sof");
    String cname(L"Long");
    sof0.open(tmp_file);
    sof0.find(cname, 0);
    
    // load the parser
    //
    parser.load(sof0);
    const String SAMPLING_FREQUENCY(L"sampling_frequency");
    Long sampling_frequency;
    sampling_frequency.readData(sof0, SAMPLING_FREQUENCY,
				parser.getEntry(sof0,SAMPLING_FREQUENCY));
    printf("sampling freq %d",(int)sampling_frequency);
    
    // close the sof file
    //
    sof0.close();
    
    if((long)sampling_frequency != (long)sample_rate_d) {
      
      // construct acknowledgement frame
      // RULE: :start_time --> speech_recognition.end_points
      // IN: :start_time :stop_time
      // OUT:
      //
      Gal_Frame frame_reset = NULL;
      frame_reset = Gal_MakeFrame("main", GAL_CLAUSE);
      Gal_SetProp(frame_reset, "reset",
		  Gal_StringObject("reset"));
      Gal_SetProp(frame_reset, ":incompatible",
		  Gal_StringObject(server_name_d));
      
      // send the frame
      //
      send_signal(frame_reset, server_data_a);
    }
    //
    // checking for sampling frequency get over here.
    
    else {
      // if log file not open, then open one
      //
      if (!file_open) {
	char *client_name = Gal_GetString(frame_a, ":client_name");
	sprintf(audio_logdir_d, "log_audio_ready_%s.txt", client_name);
	client_log_file_d = fopen(audio_logdir_d, "w");
	
	// if not able to open a log file error out
	//
	if (client_log_file_d == NULL) {
	  fprintf(stderr,"unable to open log file \n"); 
	  fflush(stderr);
	  exit(1);
	}
	file_open = true;
      }
      
      // increment the utterance counter
      //
      utt_counter_d++;
      
      // print debug information
      //
      if (DEBUG) {
	printf("audio_logdir_d = %s\n", audio_logdir_d);
      }
      char *source_server = Gal_GetString(frame_a, ":source_name");
      fprintf(stdout, "************************************************************************\n");        
      fprintf(stdout, "received audio_ready_signal (%d)\n", button_counter);
      fflush(stdout);
      
      // construct acknowledgement frame
      //
      frame = Gal_MakeFrame("main", GAL_CLAUSE);
      
      // assign the acknowlegements to the global variable
      //
      sprintf(acknowledgement_d, ":%s_ack", source_server);
      
      Gal_SetProp(frame, acknowledgement_d,
		  Gal_StringObject("audio_ready"));
      Gal_SetProp(frame, ":source_name", Gal_StringObject(server_name_d));
      Gal_SetProp(frame, ":button_press_counter", Gal_IntObject(button_counter));
      // send the frame
      //
      send_signal(frame, server_data_a);
      printf("frame sent");
      fprintf(client_log_file_d,
	      "acknowledgement for audio_ready_signal (%d) has been sent\n",
	      button_counter);
      fflush(stdout);
      server_state_d = DATA_PROCESSING;
    }
  }
  else {
    fprintf(stdout, "audio ready signal has not been received, exit\n");
    fflush(stdout);
    
    // exit the program
    //
    exit(1);
  }
}

// method: state_raw_data
//
// arguments:
//
//  Gal_Frame  frame: (input) Galaxy Frame
//  void *server_data): (input) server information
//  
// return value: 
//
// this function process frame when the system in the raw data state
//
void state_process_data(Gal_Frame frame_a, void *server_data_a) {
    
  // define local variable
  //
  Gal_Frame frame = NULL;
  String input_file_name;
  char* filename = NULL;
  
  // get the the utterance end signal if it exists
  //
  if (Gal_GetString(frame_a, ":utterance_status")) {
    
    char *source_server = Gal_GetString(frame_a, ":source_name");
    
    // assign the acknowlegements to the global variable
    //
    sprintf(acknowledgement_d, ":%s_ack", source_server);
    
    // construct acknowledgement frame
    //
    frame = Gal_MakeFrame("main", GAL_CLAUSE);
    Gal_SetProp(frame, acknowledgement_d,
		Gal_StringObject("utterance_status"));
    Gal_SetProp(frame, ":source_name", Gal_StringObject(server_name_d));
    send_signal(frame, server_data_a);
    fprintf(stdout, "total number of samples get = %d\n", total_samples_d);    
    fprintf(stdout, "acknowledgement for utterance status has been sent\n");
    fflush(stdout);
    server_state_d = WAIT_AUDIO_READY;
    return;
  }
  else if (Gal_GetString(frame_a, ":reset")) {
    server_state_d = WAIT_AUDIO_READY;
    return;
  }
  else {  
    
    // get the the utterance filename  if it exists
    //
    if (Gal_GetString(frame_a, ":filename")) {
      filename = Gal_GetString(frame_a, ":filename");
      input_file_name.assign((byte*)filename);
      Vector<VectorFloat> data_a;
      Filename input_file(input_file_name);
      
      // loop over all front ends
      //
      for (long j = 0; j < vector_fe_d.length(); j++) {
	// display a status message
	//
	String output(L"processing pfile ");
	output.concat(j + 1);
	output.concat(L": ");
	output.concat(vector_param_d(j));
	Console::putNoWrap(output);
	
	// process the utterance file by the front end
	//
	vector_fe_d(j).open(input_file);
	
	// declare local variables
	//
	long num_frames = vector_fe_d(j).getNumFrames();
	Long(num_frames).debug(L"num_frames");
	
	// set the number of feature vectors
	//
	data_a.setLength(num_frames);
	
	// loop over the feature vectors in the file
	//
	for (long ia = 0; ia < num_frames; ia++) {
	  
	  // get the feature vector
	  //      
	  vector_fe_d(j).getVector(data_a(ia), 0, (long)ia);
	  
	  // sanity check
	  //
	  if (data_a(ia).length() == 0 && j == vector_fe_d.length() - 1) {
	    Error::handle(L"speech server",
			  L"extractFeatures - observation length is zero",
			  Error::ARG, __FILE__, __LINE__);
	    return;
	  } 
	}
	fprintf(stdout, "REMOVED: outside for loop");    
	fflush(stdout);
	vector_fe_d(j).close();
      }
      int samples = data_a.length();
      
      // constructor the data format needed by the prototype system
      // for decoding
      //
      float_8* mfc_data;
      float real_time = 0;
      mfc_data = new float_8[samples * data_a(0).length()];
      audio_duration_sec_d = (float)(samples * frame_duration_d);
      printf("data length %f", data_a.length());
      printf("frame_duration_d %f", frame_duration_d);
      
      for (long ia = 0; ia < samples; ia++) {      
	for (long ja = 0; ja < data_a(0).length(); ja++) {
	  mfc_data[ia * data_a(0).length() + ja] = data_a(ia)(ja);
	}
      }
      int feature_samples = samples * data_a(0).length();
      
      // decoding procedure
      //
      if (mfc_data != NULL) {
	
       	//*********************uttproc_mfcdata**************************
	
	tp.decoder_d->reset_cc(); 
	clock_t time_start, time_stop;
	time_start = clock();
	
	// perform viterbi search
	//
	tp.decoder_d->decode_cc((double*)mfc_data, feature_samples);
	time_stop = clock();
	cpu_seconds_elapsed_d = (time_stop - time_start)/(float)CLOCKS_PER_SEC;
	printf("total_time %f",cpu_seconds_elapsed_d);
	real_time = cpu_seconds_elapsed_d/audio_duration_sec_d; 
	printf("audio_duration_sec_d %f", audio_duration_sec_d);
	printf("real_time %f",real_time);
	
	//*********************uttproc_mfcdata**************************
	
	delete [] mfc_data;
      }
      
      //**********************uttproc_rawdata**************************** 
      
      // get the hypotheses and alignment
      //
      tp.get_hypo_cc((char_1 *&)hypotheses, (char_1 *&)alignment);
      
      // clean up memory used for this utterance
      //
      tp.decoder_d->clean_cc();
      
      // clean the frontend
      //
      frontend.clean_cc();
      fprintf(stdout, "the length of hypo=%d\n", strlen((char*)hypotheses));
      fflush(stdout);
      char* hypo = NULL;
      char* align = NULL;
      if (hypo) hypo[0] = '\0';
      if (!uttproc_result(&hypo)) {
	fprintf(stdout, "Couldn't get the hypothesis");
	fflush(stdout);
      }
      else {
	fprintf(stdout,"***** N-best List: ***********************\n");
	fflush(stdout);
	fprintf(stdout, "THE HYPOTHESES %s", hypo);
	fflush(stdout);
      }
      
      if (!uttproc_alignment(&align)) {
	fprintf(stdout, "Couldn't get the alignment");
	fflush(stdout);
      }
      else {
	fprintf(stdout, "THE ALIGNMENT %s", align);
	fflush(stdout);
      } 
      
      return_recognized_speech(server_data_a, hypo,
			       cpu_seconds_elapsed_d, real_time);
      
    }
    else {
      fprintf(stdout, "Raw data does not been received, exit\n");
      fflush(stdout);
      
      // exit the program
      //
      exit(1);
    }
  }
}

// method: uttproc_result
//
// arguments: none
//  char** hypo: (input) array containing the hypotheses
//
// return: int indicating status
//
// get the final hypotheses
//
int uttproc_result(char** hypo) {
  
  if (hypo != NULL) {
    delete *hypo;
  }
  *hypo = new char[strlen((char*)hypotheses) + 1];
  sprintf(*hypo, "%s", hypotheses);
  return (int)1;
}

// method: uttproc_alignment
//
// arguments:
//  char** align: (input) array containing the alignment
//
// return: int indicating status
//
// get the alignment
//
int uttproc_alignment(char** align) {
  
  if (align != NULL) {
    delete *align;
  }
  *align = new char[strlen((char*)alignment) + 1];
  sprintf(*align, "%s", alignment);
  return (int)1;
}

/*-----------------------------------------------------------------------*
 * Routine to send the decoded speech to the Hub			 * 
 *-----------------------------------------------------------------------*/
// method: return_recognized_speech
//
// arguments:
//  void* server_data_a: (input) server information
//  char* hypo: (input) hypotheses array
//  float total_time_d: (input) total time
//  float real_time_d: (input) real time
//
// return: int indicating status
//
// recognize the speech and return the hypotheses
//
int return_recognized_speech(void *server_data_a, char *hypo, float total_time_d, float real_time_d) {
  
  int index;
  Gal_Frame f = Gal_MakeFrame("main", GAL_CLAUSE);
  Gal_Object *nbest_list;
  char* hyp = hypo, tmp_hyp[MAX_HYPO_LENGTH];
  char* word, *last;
  int num_frm = 0;
  float score = 0;
  
  // find the number of N-best list
  //
  index = 0;
  hyp = strchr(hyp, '\n');
  while (hyp) {
    hyp++;;
    index++;
    hyp = strchr(hyp, '\n');
  }    
  nbest_list = (Gal_Object*)calloc(sizeof(Gal_Object), index);
  
  // replace the head and tail by user's request and 
  // remove those likelihood scores
  //
  index = 0;
  *tmp_hyp = '\0';
  hyp = strchr(hypo, '\n');
  while (hyp) {
    
    // separate the nbest list
    //
    *hyp = '\0';
    
    // tokenize the hypothesis
    //
    while ((word = strtok_r(hypo, " ", &last)) != NULL) {
      
      // move the pointer to the next sub word
      //
      hypo = last;
      
      // remove SENT_START and SENT_END
      //
      if (strcmp(word, "!SENT_END") == 0) {
	
	// get the frame number and score
	//
	word = strtok_r(hypo, " ", &last);
	num_frm = atoi(word);
	hypo = last;
	word = strtok_r(hypo, " ", &last);
	score = atof(word);
	printf("num_frame = %d, score = %f\n", num_frm, score);
	break;
      }
      
      else if (strcmp(word, "!SENT_START") != 0) {
	strcat(tmp_hyp, word);
	strcat(tmp_hyp, " ");
      }
    }
    
    // add the specific tail
    //
    //    sprintf(&tmp_hyp[j], "%s", NBEST_TAIL);
    // ---- send frame to hub with recognizer output ----------
    //
    nbest_list[index++] = Gal_StringObject(tmp_hyp);
    hypo = hyp + 1;
    hyp = strchr(hypo, '\n');
  }
  
  // FIX: This is here because if nbest_list[0] is null then a NULLObject
  // is sent which does not trigger anything throughout the demo. This
  // means that the rest of the servers just need to make sure they know
  // about this special value return
  //
  if (nbest_list[0] == NULL) {
    nbest_list[0] = Gal_StringObject("!!NO HYPO!!");
    index = 1;
  }
  
  Gal_SetProp(f, ":input_string", nbest_list[0]);
  Gal_SetProp(f, ":nbest_list", Gal_ListObject(nbest_list, index));
  Gal_SetProp(f, ":num_frames", Gal_IntObject(num_frm));
  Gal_SetProp(f, ":path_score", Gal_FloatObject(score));
  Gal_SetProp(f, ":sysid", Gal_StringObject("si"));
  Gal_SetProp(f, ":real_time", Gal_FloatObject(real_time_d));
  Gal_SetProp(f, ":total_time", Gal_FloatObject(total_time_d));

  // send the frame
  //
  send_signal(f, server_data_a);
  free(nbest_list);
  
  return 1;
}

