

char ETX_character[]={3};
char DLE_character[]={16};  /* The "escape" character the modem uses all the time. */
char voicemdm_dtmf_string[16];       /* Used to pass/return dtmf strings and for general dtmf string manipulation. */

int voicemdm_open_modem_port(int port)
{
if (fossil_init(port))
  fatal_error("Unable to initialize communications port / fossil driver");
fossil_set_handshaking(port,2);  /* Hardware handshaking. */
fossil_set_dtr(port,1);          /* Turn on DTR to modem. */
return(0);
}

int voicemdm_close_modem_port(int port)
{
send_to_modem(port,"ATZ");
send_to_modem(port,CR_string);
if (waitfor(port,"OK",5,0))
  fatal_error("Didn't get OK after ATZ command to reset modem!");
fossil_close(port,0);   /* Close port, dropping DTR. */
return(0);
}

int voicemdm_reset_modem(int port)
{
printf("Resetting / hanging up the modem....\n",port);
wait(0);
send_to_modem(port,"AT");
send_to_modem(port,CR_string);
trace_start("OK");
trace_start("VCON");
if (waitfor_any_trace(port,5,0))
  {
  printf("Failed to get OK or VCON from 1st AT command; sending key abort\n");
  printf("and DLE/ETX to modem....\n");
  wait(1);
  send_to_modem(port,"!");
  send_to_modem(port,CR_string);
  wait(1);
  fossil_send(port,16);
  fossil_send(port,3);
  wait(5);
  }
trace_stop_all();
printf("Starting one-second wait....");
wait(1);
printf("  Done with one-second wait.\n");
trash_modem_output(port,0);  /* We've hopefully gotten the modem's attention. */
printf("Initializing modem.\n");
wait(1);
send_to_modem(port,"AT");
send_to_modem(port,CR_string);
if (waitfor(port,"OK",2,0))
  fatal_error("AT command failed to get OK from modem in 2 seconds");
wait(1);
send_to_modem(port,"ATZ");
send_to_modem(port,CR_string);
if (waitfor(port,"OK",5,0))
  fatal_error("ATZ command failed to get OK from modem in 5 seconds");
wait(1);
return(0);
}


int voicemdm_set_modem_parameters(int port)
{
printf("Setting voice operation parameters.\n");
send_to_modem(port,"AT #BDR=24 #CLS=8 #VBS=4 #VSP=50 #VBT=1");
send_to_modem(port,CR_string);
if (waitfor(port,"OK",3,0))
  return(1);
wait(1);
send_to_modem(port,"AT #VTD=3F,3F,3F #VSS=2 #VSR=7200 #VSD=0");
send_to_modem(port,CR_string);
if (waitfor(port,"OK",2,0))
  return(1);
wait(1);
trash_modem_output(port,0);
return(0);
}


/*----------------------------- Voice routines -------------------------------*/

int voicemdm_record_file(int port,char *filespec,int local_flag)
{
FILE *voice_file;
char input_char,input_char_2;
time_t ending_time;  /* Timeout variables. */
int return_code=0;
long data_characters_received_count=0,  /* Track how much voice data we've gotten. */
     data_characters_needed=1000;       /* ...needed to say we've successfully recorded something. */
voicemdm_dtmf_string[0] = 0;  /* Initialize to nulls. */
voicemdm_dtmf_string[1] = 0;
ending_time = time(NULL) + 120;     /* Max. of 120 seconds per message. */
if (local_flag)
  {
  printf("Please pick up handset and prepare to record message.\n");
  send_to_modem(port,"AT#VLS=1");
  send_to_modem(port,CRLF_string);
  if (waitfor(port,"VCON",3,0))
    fatal_error("Didn't get VCON within 3 seconds after AT#VLS=1 command.");
  pause();
  }
printf("Recording voice file from port %d to file %s; hit any key to force end.\n",port,filespec);
voice_file = fopen(filespec,"wb");
if (voice_file == NULL)
  fatal_error("Couldn't open file in record_voice_file().");
send_to_modem(port,"AT#VRX");
send_to_modem(port,CR_string);
if (waitfor(port,"CONNECT",2,0))          /* Modem didn't cooperate? */
  fatal_error("Didn't get CONNECT after AT#VRX command.");
while (1)          /* Copy characters from port to voice file. */
  {
  if (kbhit())          /* Any key hit on the keyboard? */
    {
    printf("Key hit; ending recording.\n");
    break;
    }
  if (fossil_available(port))
    {
    input_char = fossil_receive(port);
    data_characters_received_count++;
    if (input_char != 16)   /* Not the "Escape" character? */
      {
      fwrite(&input_char,1,1,voice_file);
      continue;
      }
/*    printf("DLE received; waiting for second character.\n"); */
    while (!fossil_available(port))  { };   /* Wait for 2nd character. */
    input_char_2 = fossil_receive(port);
/*    printf("The 2nd character's value was %d, picture is %c.\n",input_char_2,input_char_2); */
    switch (input_char_2)
      {
      case  3 : /* printf("DLE/ETX received!  Quitting nicely.\n"); */
                break;                  /* This is the most proper end. */
      case 'b':
      case 'd': /* printf("Busy or dialtone (%c) received; quitting nicely.\n",input_char_2); */
                if (data_characters_received_count > data_characters_needed)  /* Some voice data already successfully recorded? */
                  break;
                return_code = 1;
                break;
      case 'o': printf("DLE/o received; overrun detected!\n");
                continue;
      case 'q': /* printf("Quiet received; quitting nicely.\n"); */
                if (data_characters_received_count > data_characters_needed)
                  break;
                return_code = 1;
                break;
      case 's': /* printf("Silence received; breaking out nicely.\n"); */
                if (data_characters_received_count > data_characters_needed)
                  break;
                return_code = 1;
                break;
      case '*': /* printf("Asterisk received; exiting nicely.\n"); */
                voicemdm_dtmf_string[0] = '*';
                break;
      case '#': /* printf("Pound sign received; exiting nicely.\n"); */
                voicemdm_dtmf_string[0] = '#';
                break;                   /* Pretty much done immediately. */
      case  16: fwrite(&input_char,1,1,voice_file);
                continue;
      default : /* printf("2nd character (ignored) looked like %c with value of %d\n",input_char_2,input_char_2); */
                continue;
      }
    break;  /* If anything in the switch statement broke out, then we end up here. */
    }
  if (time(NULL) > ending_time)
    {
    printf("Record loop timed out seriously; breaking out.\n");
    send_to_modem(port,"!"); /* Send key abort to modem. */
/*    return_code = 1;  Don't zap messages that timed out for now. */
    break;
    }
  }
send_to_modem(port,"!");  /* Cause modem to quit receiving. */
fclose(voice_file);
if (waitfor(port,"VCON",10,0))
  fatal_error("Didn't get VCON after closing voice file.");
wait(1);
trash_modem_output(port,0);
/* printf("Done recording voice file.\n"); */
if  (return_code)
  {
  printf("About to execute command to delete voice file: %s\n",filespec);
  unlink(filespec);
  printf("  Because a busy tone or dial tone was received, the file was zapped.\n");
  }
return(return_code);
}


int voicemdm_play_file(int port,char *filespec,int local_flag)
{
FILE *voice_file;
char output_char,output_char_2,input_char,input_char_2;
int read_count,buffer_pointer,underflow_count=0,underflow_limit=250;
printf("Playing voice file from file %s to port %d.\n",filespec,port);
voice_file = fopen(filespec,"rb");
if (voice_file == NULL)
  fatal_error("Couldn't open file in play_voice_file().");
if (local_flag)
  {
  send_to_modem(port,"AT#VLS=1");
  send_to_modem(port,CRLF_string);
  if (waitfor(port,"VCON",3,0))
    fatal_error("Didn't get VCON 3 seconds after AT#VLS=1 command.");
  printf("Pick up the local phone and press a key when ready to listen.\n");
  pause();
  }
send_to_modem(port,"AT#VTX");
send_to_modem(port,CR_string);
if (waitfor(port,"CONNECT",2,0))          /* Modem didn't cooperate? */
  fatal_error("Didn't get CONNECT after AT#VTX command.");
voicemdm_dtmf_string[0] = 0;  /* Initialize 1st two spaces to nulls. */
voicemdm_dtmf_string[1] = 0;
if (fseek(voice_file,0L,SEEK_SET))
  fatal_error("Unable to seek to the start of the file to be played");

while (fread(&output_char,1,1,voice_file))
  {
  if (output_char == 16)                      /* Escape character?           */
    {
    fread(&output_char_2,1,1,voice_file);     /* Read next character.        */
    if (output_char_2 != 16)                  /* Something special?          */
      continue;                               /*  Then don't send it.        */
    }
  while (!fossil_ok_to_send(port))  { };
  fossil_send(port,output_char);
  /* After every character, see if anything was received from the user. */
  if (fossil_available(port))
    {
    input_char = fossil_receive(port);
    if (input_char != 16)   /* Not the "Escape" character? */
      continue;   /* Go send some more. */
/*    printf("DLE received; waiting for second character.\n"); */
    while (!fossil_available(port))  { };   /* Wait for 2nd character. */
    input_char_2 = fossil_receive(port);
/*    printf("The 2nd character's value was %d.\n",input_char_2); */
    if (input_char_2 == 'u')  /* Underflow indication received? */
      {
      if (++underflow_count == underflow_limit)
        {
        printf("Too many (%d) underflows received; quitting playing of voice file.\n",underflow_limit);
        break;
        }
      continue;
      }
    if ((isdigit(input_char_2)) || (input_char_2 == '*') || (input_char_2 == '#'))  /* Was a key received during playback of voice file? */
      {
      voicemdm_dtmf_string[0] = input_char_2;  /* Key received; quit and return key. */
      break;
      }
    /* Otherwise, just ignore the character! */
    }
  }
printf("\n");
fclose(voice_file);
/* printf("Getting ready to send DLE/ETX to the modem to end playing.\n"); */
while (!fossil_ok_to_send(port))  { };
fossil_send(port,16);
while (!fossil_ok_to_send(port))  { };
fossil_send(port,3);
if (waitfor(port,"VCON",30,0))   /* Hopefully we won't fill up 30 seconds of buffer! */
  fatal_error("Didn't get VCON within 30 seconds of sending DLE/ETX to modem.");
wait(1);
trash_modem_output(port,0);
/* printf("Done playing voice file.\n"); */
return(0);
}

/*------------------------------ DTMF routines -------------------------------*/

int voicemdm_wait_for_ring(int port,int rings_to_answer_in,int timeout_seconds)    /* Return 0 for key pressed, 1 for regular ring, and 2 for double ring, 3 for timeout, 4 for 'r' pressed (for recording), 5 for 'p' pressed. */
{
int ring_count=0,result_code;
char input_char;
time_t time_of_last_RING,timeout_time;
timeout_time = time(NULL) + timeout_seconds;  /* Calculate ending time. */
time_of_last_RING = 0;  /* Indicate that no rings have been received yet. */
if (trace_start("RING"))     /* Start trace which waits for phone call. */
  fatal_error("Couldn't start trace for RING");
while (1)   /* Wait for keypress or RING. */
  {
  if (kbhit())               /* Was a key hit? */
    {
    input_char = getch();
    if (input_char == 27)    /* Escape hit?  */
      {
      result_code = 0;
      break;
      }
    if (input_char == ' ')   /* Space key pressed (to induce timeout)? */
      {
      result_code = 3;
      break;
      }
    if (input_char == 'a')  /* Answer phone right away? */
      {
      result_code = 1;
      break;
      }
    if (input_char == 't')  /* Terminal mode requested? */
      {
      modem_terminal(port);
      continue;
      }
    if (input_char == 'r')  /* Special function (record) which causes exit with errorlevel 4. */
      {
      result_code = 4;
      break;
      }
    if (input_char == 'p')  /* Special function (play) which causes exit with errorlevel 5. */
      {
      result_code = 5;
      break;
      }
    printf("%s",BELL_string);          /* Make an error beep noise. */
    continue;
    }
  if ((time_of_last_RING) && (time(NULL) > time_of_last_RING + 7))  /* Not the first ring received, and too long between rings? */
    {
    printf("Too long between rings; ring count reset to 0.\n");
    ring_count = 0;
    result_code = 0;   /* Reset this here just to be sure. */
    time_of_last_RING = 0;  /* Reset this so that the loop doesn't repeat through this statement until timeout. */
    }
  if ((time(NULL) > timeout_time) && (!ring_count))  /* Timeout expired and we're not in the middle of receiving RINGs? */
    {
    printf("Timeout waiting for rings; breaking out.\n");
    result_code = 3;
    break;
    }
  if (!fossil_available(port))   /* No character(s) received? */
    continue;
  input_char = fossil_receive(port);  /* We got a character. */
  trace_process_received_character(input_char);
  printf("%c",input_char);
  if (!trace_fired("RING"))
    continue;
  printf("RING received.\n");
  trace_stop("RING",0);   /* RING received; process it. */
  trace_start("RING");
  if (!time_of_last_RING)  /* Is this the first ring received? */
    {
    printf("Setting result code to 1.\n");
    ring_count++;
    result_code = 1;
    }
  else
    {        
    if ((time(NULL) - time_of_last_RING) < 4)  /* Is this a double ring? */
      {
      printf("Setting result code to 2.\n");
      result_code = 2;
      }
    else
      {
      ring_count++;   /* Not the second ring of a double ring, so increment. */
      }
    }
  if (ring_count == rings_to_answer_in)  /* We've received enough RINGs? */
    {
    printf("Ring count reached; breaking out.\n");
    break;
    }
  time_of_last_RING = time(NULL);
  }
trace_stop("RING",0);
printf("Returning result code of %d.\n",result_code);
return(result_code);
}


int voicemdm_record_dtmf_key(int port,int timeout_seconds)   /* This returns the digit hit as a character or a 0 for timeout / nothing entered.  It also sets the 1st char of voicemdm_dtmf_string. */
{
char input_char;
time_t ending_time;
printf("Waiting for a DTMF key to be pushed.\n");
ending_time = time(NULL) + timeout_seconds;
while (time(NULL) < ending_time)
  {
  if (!fossil_available(port))
    continue;
  input_char = fossil_receive(port);
  if (input_char != 16)                /* Skip everything but DLE sequences. */
    continue;
/*  printf("DLE received; waiting for second character.\n"); */
  while (!fossil_available(port))  { };
  input_char = fossil_receive(port);
  if ((isdigit(input_char)) || (input_char == '#') || (input_char == '*'))
    {
    printf("Received the digit %c.\n",input_char);
    voicemdm_dtmf_string[0] = input_char;
    return(input_char);              /* Return character pressed. */
    }
  else
    { };        /* Ignore other characters. */
  }
voicemdm_dtmf_string[0] = 0;
return(0);      /* Timed out; nothing to report. */
}


int voicemdm_record_dtmf_string(int port,int timeout_seconds)
{
char input_char;
int counter=0;
time_t ending_time;  /* Timeout variables. */
printf("Recording DTMF tones now....\n");
memset(voicemdm_dtmf_string,0,sizeof(voicemdm_dtmf_string));  /* Clear out field. */
ending_time = time(NULL) + timeout_seconds;
while (time(NULL) < ending_time)  /* If this happens, we timed out, and return 1. */
  {
  if (!fossil_available(port))
    continue;
  input_char = fossil_receive(port);
  if (input_char != 16)   /* Not the "Escape" character? */
    continue;
/*  printf("DLE received; waiting for second character.\n"); */
  while (!fossil_available(port))  { };   /* Wait for 2nd character. */
  input_char = fossil_receive(port);
/*  printf("The 2nd character's value was %d.\n",input_char); */
  if (input_char == '#')
    {
    printf("Got #; terminated dtmf string.\n");
    return(0);
    }
  if ((input_char >= '0') && (input_char <= '9'))   /* Did we get a digit? */
    {
    voicemdm_dtmf_string[counter++] = input_char;
    printf("Received the digit %c.\n",input_char);
    if (counter == sizeof(voicemdm_dtmf_string))  /* String filled up? */
      {
      printf("DTMF string full; exiting.\n");
      return(2);              /* Return "informational" error code. */
      }
    }
  else
    { };        /* Ignore other characters. */
  }
memset(voicemdm_dtmf_string,0,sizeof(voicemdm_dtmf_string));  /* Clear out string. */
printf("Timed out without receiving terminator; quitting.\n");
return(1);
}

int voicemdm_play_beep(int port)
{
send_to_modem(port,"at #vts=0");
send_to_modem(port,CR_string);
if (waitfor(port,"OK",3,0))
  {
  printf("Didn't get OK from modem after sending beep tone.\n");
  return(1);
  }
return(0);
}


int voicemdm_play_dtmf_string(int port)
{
int digit_number=0;
printf("Playing the dtmf string %s now....\n",voicemdm_dtmf_string);
while (digit_number < strlen(voicemdm_dtmf_string))
  {
  send_to_modem(port,"at #vts=");
  printf("Waiting to send touch tone digit %c to the modem.\n",voicemdm_dtmf_string[digit_number]);
  while (!fossil_ok_to_send(port))  { };
  fossil_send(port,voicemdm_dtmf_string[digit_number++]);
  send_to_modem(port,CR_string);
  if (waitfor(port,"OK",3,0))
    {
    printf("Didn't get OK from modem after sending #vts=x");
    return(1);
    }
  wait(0);
  }
return(0);
}


