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

Routine:
  AFILE *AFgetESpar (FILE *fp, const char Fname[], long int *Nsamp,
                     long int *Nchan, float *Sfreq, FILE *fpout)

Purpose:
  Get file format information from an ESPS sampled data feature file

Description:
  This routine reads the header for an ESPS sampled data feature file.  The
  header information is used to set the file data format information in the
  audio file pointer structure and to set the returned argument values. A
  banner identifying the audio file and its parameters is printed.

  ESPS sampled data feature file header:
   Offset Length Type    Contents
      8     4    --     Header size (bytes)
     12     4    int    Sampled data record size
     16     4    int    File identifier
     40    26    char   File creation date
    124     4    int    Number of samples (may indicate zero)
    132     4    int    Number of doubles in a data record
    136     4    int    Number of floats in a data record
    140     4    int    Number of longs in a data record
    144     4    int    Number of shorts in a data record
    148     4    int    Number of chars in a data record
    160     8    char   User name
    333    ...   --     Generic header items, including "record_freq"
      -    ...   --     Audio data
  16-bit integer and 32-bit floating-point data formats are supported.

Parameters:
  <-  AFILE *AFgetESpar
      Audio file pointer for the audio file
   -> FILE *fp
      File pointer for the file
   -> const char Fname[]
      File name
  <-  long int *Nsamp
      Total number of samples in the file (all channels)
  <-  long int *Nchan
      Number of channels
  <-  float *Sfreq
      Sampling frequency from the file header
   -> FILE *fpout
      File pointer for printing the audio file identification information.  If
      fpout is NULL, no information is printed.

Author / revision:
  P. Kabal  Copyright (C) 1996
  $Revision: 1.41 $  $Date: 1996/08/14 17:55:55 $

-------------------------------------------------------------------------*/

static char rcsid [] = "$Id: AFgetESpar.c 1.41 1996/08/14 AFsp-V2R1 $";

#include <stdio.h>
#include <string.h>
#include <libtsp.h>
#include <libtsp/nucleus.h>
#include <libtsp/AFpar.h>
#include <libtsp/ESpar.h>
#include <libtsp/UTtypes.h>

#define MINV(a, b)	(((a) < (b)) ? (a) : (b))

#define RHEAD_SN(fp,offs,string,N) \
			AFreadHead (fp, (long int) (offs), (void *) (string), \
				    1, N, DS_NATIVE)
#define RHEAD_S(fp,offs,string) \
			AFreadHead (fp, (long int) (offs), (void *) (string), \
				    1, sizeof (string), DS_NATIVE)
#define RHEAD_V(fp,offs,value,swap) \
			AFreadHead (fp, (long int) (offs), (void *) &(value), \
				    sizeof (value), 1, swap)
#define SAME_CSTR(str,ref) 	(memcmp (str, ref, sizeof (str)) == 0)

AFILE *
AFgetESpar (fp, Fname, Nsamp, Nchan, Sfreq, fpout)

     FILE *fp;
     const char Fname[];
     long int *Nsamp;
     long int *Nchan;
     float *Sfreq;
     FILE *fpout;

{
  struct ES_preamb Fpreamb;
  struct ES_head Fhead;
  AFILE *AFp;
  int Lw, Format, Fbo;
  long int Nbytes, Nsampx, offs, Nchanx, Ldata;
  int Ninfo;
  char Cinfo[ES_MAXINFO+1];
  char *p;
  double8_t FSfreq;

/* Get the size of the file */
  Nbytes = FLfileSize (fp);
  if (Nbytes < ES_LHMIN)
    UThalt ("AFgetESpar: ESPS file header too short");

/* Check the preamble file magic */
  RHEAD_S (fp, 16L, Fpreamb.Magic);
  if (SAME_CSTR (Fpreamb.Magic, FM_ESPS))
    Fbo = DS_EB;
  else if (SAME_CSTR (Fpreamb.Magic, FM_ESPS_SWAPPED))
    Fbo = DS_EL;
  else
    UThalt ("AFgetESpar: Invalid ESPS file identifier");

/* Read selected preamble values */
  offs = 8L;
  offs += RHEAD_V (fp, offs, Fpreamb.Data_offset, Fbo);
  RHEAD_V (fp, offs, Fpreamb.Record_size, Fbo);

/* Read selected header values */
  offs = 32L;
  offs += RHEAD_V (fp, offs, Fhead.Type, Fbo);
  offs += 2;
  offs += RHEAD_S (fp, offs, Fhead.Magic);
  offs += RHEAD_S (fp, offs, Fhead.Datetime);
  offs += 58;
  offs += RHEAD_V (fp, offs, Fhead.Ndrec, Fbo);
  offs += 8;
  offs += RHEAD_V (fp, offs, Fhead.Nfloat, Fbo);
  offs += 4;
  offs += RHEAD_V (fp, offs, Fhead.Nshort, Fbo);
  offs += RHEAD_V (fp, offs, Fhead.Nchar, Fbo);
  
  if (Fhead.Type != ES_FTYPE)
    UThalt ("AFgetESpar: Unsupported ESPS file type");
  if (! SAME_CSTR (Fhead.Magic, Fpreamb.Magic))
    UThalt ("AFgetESpar: ESPS file identifier values do not match");

/* Get the data format */
  if (Fhead.Nshort != 0) {
    Lw = FDL_INT16;
    Nchanx = Fhead.Nshort;
    Format = FD_INT16;
  }
  else if (Fhead.Nfloat != 0) {
    Lw = FDL_FLOAT32;
    Nchanx = Fhead.Nfloat;
    Format = FD_FLOAT32;
  }
  else
    UThalt ("AFgetESpar: Unsupported ESPS file data format");

  if (Fpreamb.Record_size != Lw * Nchanx)
    UThalt ("AFgetESpar: Unsupported ESPS file data encoding");

/* Warnings, error checks */
  if (Fpreamb.Data_offset < ES_LHMIN || Fpreamb.Data_offset > Nbytes)
    UThalt ("AFgetESpar: Invalid data offset value");
  if (Fhead.Ndrec > 0)
    Ldata = Fhead.Ndrec * Lw * Nchanx;
  else
    Ldata = Nbytes - Fpreamb.Data_offset;
  if (Ldata > Nbytes - Fpreamb.Data_offset)
    UThalt ("AFgetESpar: Invalid header data length field");
  if (Ldata != Nbytes - Fpreamb.Data_offset)
    UTwarn ("AFgetESpar - Invalid header data length field");

/* Get the sampling frequency from the "record_freq" record */
  Ninfo = MINV (ES_MAXINFO, Fpreamb.Data_offset - ES_LHMIN);
  RHEAD_SN (fp, ES_LHMIN, Cinfo, Ninfo);
  p = STstrstrNM (Cinfo, "record_freq", Ninfo, 12);
  if (p != NULL) {
    offs = ES_LHMIN + (p - Cinfo) - 4;	/* Beginning of record */
    RHEAD_V (fp, offs + 22L, FSfreq, Fbo);
  }
  else {
    UThalt ("AFgetESpar: Unable to determine sampling frequency");
  }

/* Set the parameters for file access */
  Nsampx = Ldata / Lw;
  AFp = AFsetAFp (fp, FO_RO, FT_ESPS, Format, Fbo, 1.0, Nchanx,
		  (long int) Fpreamb.Data_offset, Ldata, Nsampx);

/* Check and print the header information */
  AFprintAFh (AFp, Fname, Fhead.Datetime, FSfreq, fpout);

/* Set the return parameters */
  *Nsamp = Nsampx;
  *Nchan = Nchanx;
  *Sfreq = FSfreq;

  return AFp;
}