/*-------------- Telecommunications & Signal Processing Lab ---------------
                             McGill University

Routine:
  AFILE *AFrdSPhead (FILE *fp)

Purpose:
  Get file format information from a NIST SPHERE audio file

Description:
  This routine reads the header for a NIST SPHERE audio file.  The header
  information is used to set the file data format information in the audio
  file pointer structure.

  NIST SPHERE audio file header:
   Offset Length Type    Contents
      0     8    char   File identifier ("NIST_1A\n")
      8     8    char   Header length in ASCII ("   1024\n")
     16    ...   struct Object-oriented fields
   1024    ...   --     Audio data
  8-bit mu-law and 16-bit integer data formats are supported.

Parameters:
  <-  AFILE *AFrdSPhead
      Audio file pointer for the audio file
   -> FILE *fp
      File pointer for the file

Author / revision:
  P. Kabal  Copyright (C) 1998
  $Revision: 1.62 $  $Date: 1998/06/19 14:29:47 $

-------------------------------------------------------------------------*/

static char rcsid [] = "$Id: AFrdSPhead.c 1.62 1998/06/19 libtsp-v3r0 $";

#include <setjmp.h>
#include <string.h>

#include <libtsp.h>
#include <libtsp/AFheader.h>
#include <libtsp/AFmsg.h>
#include <libtsp/AFpar.h>

#define SAME_CSTR(str,ref) 	(memcmp (str, ref, sizeof (str)) == 0)
#define GET_REC(field,rec,nr,type,vp,errcont) \
	AFgetSPrec (field, type, (void *) vp, errcont, rec, nr)

#define LHEAD		1024L

/* NIST SPHERE file identifier as a character string */
#define SP_ident	"NIST_1A\n"
#define SP_hsize	"   1024\n"
#define SP_LI		((sizeof (SP_ident)) - 1)
#define SP_LH		((sizeof (SP_hsize)) - 1)
#define SP_LR		(LHEAD - SP_LI - SP_LH)

#define T_INTEGER	0
#define T_REAL		1
#define T_STRING	2

#define MAXSTRING	80

#define ERR_NOMSG	0
#define ERR_MSG		1

#define REQUIRED	(ERR_MSG)
#define OPTIONAL	(ERR_NOMSG)

struct SP_head {
  char ident[SP_LI];
  char hsize[SP_LH];
  char rec[SP_LR];
};

/* Comment field ID's */
static const char *CommentID[] = {
  "conversation_id",
  "database_id",
  "database_version",
  "microphone",
  "prompt_id",
  "recording_date",
  "recording_environment",
  "recording_site",
  "recording_time",
  "speaker_sentence_number",
  "speaker_session_number",
  "speaker_id",
  "speaker_id_a",
  "speaker_id_b",
  "speaking_mode",
  "utterance_id",
  NULL
};

/* setjmp / longjmp environment */
extern jmp_buf AFR_JMPENV;

static int
AF_getFormat p_((const char Rec[], int NR, int *Fbo, int *Format));
static int
AF_getNchan p_((const char Rec[], int NR, long int *Nchan)); 
static int
AF_getComment p_((const char *RecID[], const char Rec[], int NR,
		  struct AF_info *Hinfo));


AFILE *
AFrdSPhead (fp)

     FILE *fp;

{
  AFILE *AFp;
  int Format, Fbo;
  long int Srate, Nsamp, Nchan;
  char Info[AF_MAXINFO];
  struct AF_info Hinfo;
  struct SP_head Fhead;

/* Set the long jump environment; on error return a NULL */
  if (setjmp (AFR_JMPENV))
    return NULL;	/* Return from a header read error */

/* Read in the header */
  RHEAD_S (fp, Fhead.ident);
  RHEAD_S (fp, Fhead.hsize);
  RHEAD_S (fp, Fhead.rec);

/* Check the file identifier */
  if (! SAME_CSTR (Fhead.ident, SP_ident)) {
    UTwarn ("AFrdSPhead - %s", AFM_SP_BadId);
    return NULL;
  }
  if (! SAME_CSTR (Fhead.hsize, SP_hsize)) {
    UTwarn ("AFrdSPhead - %s: \"%s\"", AFM_SP_BadHLen, Fhead.hsize);
    return NULL;
  }

/* Nsamp and Srate */
  if (GET_REC ("sample_count", Fhead.rec, SP_LR, T_INTEGER, &Nsamp, REQUIRED))
    return NULL;
  if (GET_REC ("sample_rate", Fhead.rec, SP_LR, T_INTEGER, &Srate, REQUIRED))
    return NULL;

/* Sample format */
  if (AF_getFormat (Fhead.rec, SP_LR, &Fbo, &Format))
    return NULL;

/* Comment fields */
  Hinfo.Info = Info;
  Hinfo.N = 0;
  if (AF_getComment (CommentID, Fhead.rec, SP_LR, &Hinfo))
    return NULL;

/* Get the number of channels */
  if (AF_getNchan (Fhead.rec, SP_LR, &Nchan))
    return NULL;

/* Set the parameters for file access */
  AFp = AFsetRead (fp, FT_SPHERE, Format, Fbo, (double) Srate, 1.0,
		   Nchan, AF_LDATA_UNDEF, Nsamp, &Hinfo, AF_NOFIX);

  return AFp;
}

/* Decode and check the coding format */


static int
AF_getFormat (Rec, NR, Fbo, Format)

     const char Rec[];
     int NR;
     int *Fbo;
     int *Format;

{
  char hstring[MAXSTRING+1];
  int nbis, Lw, ErrCode;

  /* Get the coding format (default "pcm") */
  strcpy (hstring, "pcm");	/* Default value */
  if (GET_REC ("sample_coding", Rec, NR, T_STRING, hstring, OPTIONAL) == 2)
    return 2;

  if (GET_REC ("sample_n_bytes", Rec, NR, T_INTEGER, &Lw, REQUIRED))
    return 2;

  /* Check the data format fields */
  if (strcmp (hstring, "pcm") == 0) {

    /* PCM */
    *Fbo = DS_NATIVE;
    *Format = FD_INT16;
    nbis = 16;

    if (Lw != 2) {
      UTwarn ("AFrdSPhead - %s: \"%d\"", AFM_SP_UnsPCM, Lw);
      return 1;
    }

    ErrCode = GET_REC ("sample_byte_format", Rec, NR, T_STRING, hstring,
		       OPTIONAL);
    if (ErrCode == 2)
      return 2;
    if (ErrCode == 0) {
      if (strcmp (hstring, "10") == 0)
	*Fbo = DS_EB;
      else if (strcmp (hstring, "01") == 0)
	*Fbo = DS_EL;
      else {
	UTwarn ("AFrdSPhead - %s: \"%.20s\"", AFM_SP_UnsByte,  hstring);
	return 1;
      }
    }

    if (GET_REC ("sample_sig_bits", Rec, NR, T_INTEGER, &nbis, OPTIONAL) == 2)
      return 2;
    if (nbis != 16) {
      UTwarn ("AFrdSPhead - %s: \"%ld\")", AFM_SP_UnsRes, nbis);
      return 1;
    }
  }

  else if (strcmp (hstring, "ulaw") == 0 || strcmp (hstring, "mu-law") == 0) {

    /* Mu-law */
    *Fbo = DS_NATIVE;
    *Format = FD_MULAW8;
    if (Lw != 1) {
      UTwarn ("AFrdSPhead - %s: \"%d\"", AFM_SP_UnsMulaw, Lw);
      return 1;
    }
  }

  else {
    UTwarn ("AFrdSPhead - %s: \"%s\"", AFM_SP_UnsData, hstring);
    return 1;
  }

  return 0;
}

/* Get and check the number of channels */


static int
AF_getNchan (Rec, NR, Nchan)

     const char Rec[];
     int NR;
     long int *Nchan; 

{
  char hstring[MAXSTRING+1];

  if (GET_REC ("channel_count", Rec, NR, T_INTEGER, Nchan, REQUIRED))
    return 2;

  if (*Nchan > 1) {
    hstring[0] = '\0';
    if (GET_REC ("channels_interleaved", Rec, NR, T_STRING, hstring, OPTIONAL)
	== 2)
      return 2;
    if (strcmp (hstring, "TRUE") != 0)
      UTwarn ("AFrdSPhead - %s", AFM_SP_NoInter);
  }

  return 0;
}

/* Put comment strings into the AFsp information structure */


static int
AF_getComment (ID, Rec, NR, Hinfo)

     const char *ID[];
     const char Rec[];
     int NR;
     struct AF_info *Hinfo;

{
  char hstring[MAXSTRING+1];
  int i, N;

  for (i = 0; ID[i] != NULL; ++i) {
    hstring[0] = '\0';
    GET_REC (ID[i], Rec, NR, T_STRING, hstring, OPTIONAL);
    if (hstring[0] != '\0') {
      N = Hinfo->N;
      strcpy (&Hinfo->Info[N], ID[i]);
      strcat (&Hinfo->Info[N], ": ");
      strcat (&Hinfo->Info[N], hstring);
      Hinfo->N += strlen (&Hinfo->Info[N]) + 1;
    }
  }

  return 0;
}
