/* Copyright (c) 1991-2002 Doshita Lab. Speech Group, Kyoto University */
/* Copyright (c) 2000-2002 Speech and Acoustics Processing Lab., NAIST */
/*   All rights reserved   */

/* adin_file.c --- A/D-in functions for RAW, WAV file */

/* $Id: adin_file.c,v 1.4 2002/09/11 22:01:50 ri Exp $ */

/* sampling formats other than 16bit,mono, will be rejected */
/* sampling frequency is checked only for WAV file */

/* assume sizeof(int)=4, sizeof(short)=2 */

#include <sent/stddefs.h>
#include <sent/speech.h>
#include <sent/adin.h>

static FILE *fp;
static boolean wav_p;
static int maxlen, nowlen;
static SP16 pre_data[2];
static boolean has_pre;

static int sfreq;		/* frequency (Hz) */

/* read .wav data with endian conversion */
/* (all .wav datas are in little endian) */
static boolean
myread(void *buf, size_t unitbyte, int unitnum, FILE *fp)
{
  int tmp;
  if ((tmp = myfread(buf, unitbyte, unitnum, fp)) < unitnum) {
    return(FALSE);
  }
#ifdef WORDS_BIGENDIAN
  swap_bytes(buf, unitbyte, unitnum);
#endif
  return(TRUE);
}
#define MYREAD(A,B,C,D)  if (!myread(A, B, C, D)) {j_printerr("adin_file: file is corrupted\n"); return -1;}


/* scan Microsoft WAVEfmt and check the format */
static boolean
setup_wav()
{
  char dummy[9];
  unsigned int i, len;
  unsigned short s;

#if 0				/* already done in adin_file_open() below */
  /* first 4 byte: 'R' 'I' 'F' 'F' */
  MYREAD(dummy, 1, 4, fp);
  if (dummy[0] != 'R' ||
      dummy[1] != 'I' ||
      dummy[2] != 'F' ||
      dummy[3] != 'F') {
    j_printerr("adin_file: Not a 'RIFF' format\n");
    fclose_readfile(fp); return FALSE;
  }
#endif
  /* 4 byte: byte num of rest ( = filesize - 8) */
  /* --- just skip */
  MYREAD(dummy, 1, 4, fp);
  /* first part: WAVE format specifications */
  /* 8 byte: 'W' 'A' 'V' 'E' 'f' 'm' 't' ' ' */
  MYREAD(dummy, 1, 8, fp);
  if (dummy[0] != 'W' ||
      dummy[1] != 'A' ||
      dummy[2] != 'V' ||
      dummy[3] != 'E' ||
      dummy[4] != 'f' ||
      dummy[5] != 'm' ||
      dummy[6] != 't' ||
      dummy[7] != ' ') {
    j_printerr("adin_file: WAVEfmt part not found, file corrupted?\n");
    fclose_readfile(fp); return FALSE;
  }
  /* 4byte: byte size of this part (fixed to 16) */
  MYREAD(&i, 4, 1, fp);
  if (i != 16) {
    j_printerr("adin_file: unknown WAVEfmt part, length other than 16 bytes\n");
    fclose_readfile(fp); return FALSE;
  }
  /* 2byte: data format */
  MYREAD(&s, 2, 1, fp);
  if (s != 1) {
    j_printerr("adin_file: data format != PCM (id=%d)\n", s);
    fclose_readfile(fp); return FALSE;
  }
  /* 2byte: channel num */
  MYREAD(&s, 2, 1, fp);
  if (s >= 2) {
    j_printerr("adin_file: channel num != 1 (%d)\n", s);
    fclose_readfile(fp); return FALSE;
  }
  /* 4byte: sampling rate */
  MYREAD(&i, 4, 1, fp);
  if (i != sfreq) {
    j_printerr("adin_file: sampling rate != %d (%d)\n", sfreq, i);
    fclose_readfile(fp); return FALSE;
  }
  /* 4byte: bytes per second */
  MYREAD(&i, 4, 1, fp);
  if (i != sfreq * sizeof(SP16)) {
    j_printerr("adin_file: bytes per second != %d (%d)\n", sfreq * sizeof(SP16), i);
    fclose_readfile(fp); return FALSE;
  }
  /* 2bytes: bytes per frame ( = (bytes per sample) x channel ) */
  MYREAD(&s, 2, 1, fp);
  if (s != 2) {
    j_printerr("adin_file: (bytes per sample) x channel != 2 (%d)\n", s);
    fclose_readfile(fp); return FALSE;
  }
  /* 2bytes: bits per sample */
  MYREAD(&s, 2, 1, fp);
  if (s != 16) {
    j_printerr("adin_file: bits per sample != 16 (%d)\n", s);
    fclose_readfile(fp); return FALSE;
  }
  /* end of WAVEfmt part */

  /* seek for 'data' part */
  while (myread(&dummy, 1, 4, fp)) {
    MYREAD(&len, 4, 1, fp);
    if (dummy[0] == 'd' &&
	dummy[1] == 'a' &&
	dummy[2] == 't' &&
	dummy[3] == 'a') {
      break;
    }
    for (i=0;i<len;i++) myread(&dummy, 1, 1, fp);
  }
  /* ready to read in "data" part --- this is speech data */
  maxlen = len / sizeof(SP16);
  nowlen = 0;
  return TRUE;
}


/* open wavefile (RAW/WAV format) */
static boolean
adin_file_open(char *filename)	/* NULL for standard input */
{
  char dummy[4];

  if (filename != NULL) {
    if ((fp = fopen_readfile(filename)) == NULL) {
      j_printerr("failed to open %s\n",filename);
      return(FALSE);
    }
  } else {
    fp = stdin;
  }
  /* check first 4 byte to detect Microsoft WAVE format */
  if (myfread(dummy, 1, 4, fp) < 4) {
    j_printerr("Error: size less than 4 bytes?\n",filename);
    fclose_readfile(fp);
    return(FALSE);
  }
  if (dummy[0] == 'R' &&
      dummy[1] == 'I' &&
      dummy[2] == 'F' &&
      dummy[3] == 'F') {
    /* it's a WAVE file */
    wav_p = TRUE;
    if (setup_wav() == FALSE) {
      j_printerr("Error: failed to read %s as a wav file\n",filename);
      fclose_readfile(fp);
      return(FALSE);
    }
  } else {
    /* read as raw format file */
    wav_p = FALSE;
    memcpy(pre_data, dummy, 4);    /* already read (4/sizeof(SP)) samples */
    has_pre = TRUE;
  }
  return(TRUE);
}

/* close wavefile */
static boolean
adin_file_close()
{
  if (fclose_readfile(fp) != 0) {
    perror("adin_file_close: fclose_readfile");
    return FALSE;
  }
 return TRUE; 
}


/***** global functions *****/

/* get 1 line input from stdin with prompt */
/* return value: newly allocated buffer */
/* repeat if no input, and */
/* returns NULL on EOF */
char *
get_line(char *prompt)
{
  char *buf = NULL;
  char *p;

  buf = (char *)mymalloc(500);
  do {
    j_printerr("%s",prompt);
    if (fgets(buf, 500, stdin) == NULL) {
      free(buf);
      return(NULL);
    }
  } while (!buf[0]);		/* loop till some input */
  /* chop last newline */
  p = buf + strlen(buf) - 1;
  if (*p == '\n') {
    *(p --) = '\0';
  }
  /* chop last space */
  while(p >= buf && *p == ' ') {
    *(p --) = '\0';
  }

  return(buf);
}

static boolean from_file;	/* TRUE is reading filename from listfile */
static FILE *fp_list;		/* used for the listfile */

boolean
adin_file_standby(int freq, void *arg)	/* arg: listfilename (NULL if not) */
{
  char *fname = arg;
  if (fname != NULL) {
    /* read input filename from file */
    if ((fp_list = fopen(fname, "r")) == NULL) {
      j_printerr("failed to open %s\n", fname);
      return(FALSE);
    }
    from_file = TRUE;
  } else {
    /* read filename from stdin */
    from_file = FALSE;
  }
  /* store sampling frequency */
  sfreq = freq;
  
  return(TRUE);
}
    
boolean
adin_file_begin()
{
  char *speechfilename;
  boolean readp;

  /* ready to read next input */
  readp = FALSE;
  while(readp == FALSE) {
    if (from_file) {
      /* read file name from listfile */
      speechfilename = (char *)mymalloc(500);
      do {
	if (getl(speechfilename, 500, fp_list) == NULL) { /* end of input */
	  free(speechfilename);
	  fclose(fp_list);
	  return(FALSE); /* end of input */
	}
      } while (speechfilename[0] == '#'); /* skip comment */
    } else {
      /* read file name from stdin */
      speechfilename = get_line("enter filename->");
      if (speechfilename == NULL) return (FALSE);	/* end of input */
    }
    /* open input file */
    if (adin_file_open(speechfilename) == FALSE) {
      j_printerr("Error in reading speech data: \"%s\"\n",speechfilename);
    } else {
      j_printf("\ninput speechfile: %s\n",speechfilename);
      readp = TRUE;
    }
    free(speechfilename);
  }
  return TRUE;
}

/* try to read `sampnum' samples and returns actual sample num recorded */
/* return -1 on error or end of file */
int
adin_file_read(SP16 *buf, int sampnum)
{
  int cnt;
  if (wav_p) {
    cnt = myfread(buf, sizeof(SP16), sampnum, fp);
    if (nowlen + cnt > maxlen) {
      cnt = maxlen - nowlen;
    }
  } else {
    if (has_pre) {
      buf[0] = pre_data[0]; buf[1] = pre_data[1];
      has_pre = FALSE;
      cnt = myfread(&(buf[2]), sizeof(SP16), sampnum - 2, fp);
      if (cnt > 0) cnt += 2;
    } else {
      cnt = myfread(buf, sizeof(SP16), sampnum, fp);
    }
  }
  if (cnt == 0) {		/* error or EOF */
    if (ferror(fp)) {		/* error */
      perror("adin_file_read");
      adin_file_close();
      return -2;		/* error */
    }
    return -1;			/* EOF */
  }
  /* all .wav data are in little endian */
  /* assume .raw data are in big endian */
#ifdef WORDS_BIGENDIAN
  if (wav_p) swap_sample_bytes(buf, cnt);
#else
  if (!wav_p) swap_sample_bytes(buf, cnt);
#endif
  return cnt;
}

/* end recording */
boolean
adin_file_end()
{
  /* nothing needed */
  adin_file_close();
  return TRUE;
}


/* for standard input */
boolean
adin_stdin_standby(int freq, void *arg)	/* no arg */
{
  /* store sampling frequency */
  sfreq = freq;
  return(TRUE);
}

boolean
adin_stdin_begin()
{
  char *speechfilename;
  boolean readp;

  if (feof(stdin)) {		/* already reached the end of input stream */
    return FALSE;		/* terminate search here */
  } else {
    /* open input stream */
    if (adin_file_open(NULL) == FALSE) {
      j_printerr("Error in reading speech data from stdin\n");
    }
    j_printf("Reading wavedata from stdin...\n");
  }
  return TRUE;
    
}

int
adin_stdin_read(SP16 *buf, int sampnum)
{
  int cnt;
  if (wav_p) {
    cnt = fread(buf, sizeof(SP16), sampnum, fp); /* not gzip format */
    if (nowlen + cnt > maxlen) {
      cnt = maxlen - nowlen;
    }
  } else {
    if (has_pre) {
      buf[0] = pre_data[0]; buf[1] = pre_data[1];
      has_pre = FALSE;
      cnt = fread(&(buf[2]), sizeof(SP16), sampnum - 2, fp);
      if (cnt > 0) cnt += 2;
    } else {
      cnt = fread(buf, sizeof(SP16), sampnum, fp);
    }
  }
  if (cnt == 0) {		/* error or EOF */
    if (ferror(fp)) {		/* error */
      perror("adin_stdin_read");
      return -2;		/* error */
    }
    return -1;			/* EOF */
  }
  /* all .wav data are in little endian */
  /* assume .raw data are in big endian */
#ifdef WORDS_BIGENDIAN
  if (wav_p) swap_sample_bytes(buf, cnt);
#else
  if (!wav_p) swap_sample_bytes(buf, cnt);
#endif
  return cnt;
}
