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

Routine:
  AFILE *AFrdEShead (FILE *fp)

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.

  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, 32-bit floating-point and 64-bit floating-point data formats
  are supported.

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

Author / revision:
  P. Kabal  Copyright (C) 1998
  $Revision: 1.57 $  $Date: 1998/06/26 20:36:29 $

-------------------------------------------------------------------------*/

static char rcsid [] = "$Id: AFrdEShead.c 1.57 1998/06/26 libtsp-v3r0 $";

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

#include <libtsp.h>
#include <libtsp/nucleus.h>
#include <libtsp/AFheader.h>
#include <libtsp/AFmsg.h>
#define AF_DATA_LENGTHS
#include <libtsp/AFpar.h>
#include <libtsp/ESpar.h>
#include <libtsp/UTtypes.h>

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

#define SAME_CSTR(str,ref) 	(memcmp (str, ref, sizeof (str)) == 0)
#define SWAPB(value) \
	VRswapBytes ((const void *) &(value), \
		     (void *) &(value), sizeof (value), 1)

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

static double
AF_RecFreq p_((const char GenItems[], int N, int Fbo));


AFILE *
AFrdEShead (fp)

     FILE *fp;

{
  AFILE *AFp;
  int Format, Fbo, N;
  long int Nsamp, offs, Nchan;
  double Sfreq;
  struct AF_info Hinfo;
  char Info[AF_MAXINFO];
  struct ES_preamb Fpreamb;
  struct ES_fixhead FheadF;
  struct ES_FEAhead FheadV;
  char GenItems[ES_MAXGENERIC];

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

/* Read selected preamble values */
/* We do not know the byte order until after we have read the file magic */
  offs = RSKIP (fp, 8L);
  offs += RHEAD_V (fp, Fpreamb.Data_offset, DS_NATIVE);
  offs += RHEAD_V (fp, Fpreamb.Record_size, DS_NATIVE);
  offs += RHEAD_S (fp, Fpreamb.Magic);

/* Check the preamble file magic */
  if (SAME_CSTR (Fpreamb.Magic, FM_ESPS))
    Fbo = DS_EB;
  else if (SAME_CSTR (Fpreamb.Magic, FM_ESPS_SWAPPED))
    Fbo = DS_EL;
  else {
    UTwarn ("AFrdEShead - %s", AFM_ES_BadId);
    return NULL;
  }

/* Fix up the words we have already read */
  if (UTswapCode (Fbo) == DS_SWAP) {
    SWAPB (Fpreamb.Data_offset);
    SWAPB (Fpreamb.Record_size);
  }
 
/* Read selected values from the fixed part of the header */
  offs += RSKIP (fp, 32 - offs);
  offs += RHEAD_V (fp, FheadF.Type, Fbo);
  offs += RSKIP (fp, 2);
  offs += RHEAD_S (fp, FheadF.Magic);
  Hinfo.Info = Info;
  Hinfo.N = 0;
  offs += AFrdHtext (fp, 26, "date: ", &Hinfo, 1);
  offs += AFrdHtext (fp,  8, "header_version: ", &Hinfo, 1);
  offs += AFrdHtext (fp, 16, "program_name: ", &Hinfo, 1);
  offs += AFrdHtext (fp,  8, "program_version: ", &Hinfo, 1);
  offs += AFrdHtext (fp, 26, "program_compile_date: ", &Hinfo, 1);
  offs += RHEAD_V (fp, FheadF.Ndrec, Fbo);
  offs += RSKIP (fp, 4);
  offs += RHEAD_V (fp, FheadF.Ndouble, Fbo);
  offs += RHEAD_V (fp, FheadF.Nfloat, Fbo);
  offs += RSKIP (fp, 4);
  offs += RHEAD_V (fp, FheadF.Nshort, Fbo);
  offs += RHEAD_V (fp, FheadF.Nchar, Fbo);
  offs += RSKIP (fp, 8);
  offs += AFrdHtext (fp,  8, "user: ", &Hinfo, 1);

/* Read selected feature file header values */
  offs += RSKIP (fp, 188 - offs);
  offs += RHEAD_V (fp, FheadV.Fea_type, Fbo);

/* Generic items */
  N = MINV (ES_MAXGENERIC, Fpreamb.Data_offset - ES_LHMIN);
  offs += RSKIP (fp, ES_LHMIN - offs);
  offs += RHEAD_SN (fp, GenItems, N);

/* Skip to the start of data */
  RSKIP (fp, Fpreamb.Data_offset - offs);

/* Error checks */
  if (FheadF.Type != ES_FT_FEA) {
    UTwarn ("AFrdEShead - %s: \"%d\"", AFM_ES_UnsType, (int) FheadF.Type);
    return NULL;
  }
  if (! SAME_CSTR (FheadF.Magic, Fpreamb.Magic)) {
    UTwarn ("AFrdEShead - %s", AFM_ES_IdMatch);
    return NULL;
  }
  if (FheadV.Fea_type != ES_FEA_SD) {
     UTwarn ("AFrdEShead - %s: \"%d\"", AFM_ES_UnsFea, (int) FheadV.Fea_type);
     return NULL;
  }

/* Determine the data format */
  if (FheadF.Nshort != 0) {
    Nchan = FheadF.Nshort;
    Format = FD_INT16;
  }
  else if (FheadF.Nfloat != 0) {
    Nchan = FheadF.Nfloat;
    Format = FD_FLOAT32;
  }
  else if (FheadF.Ndouble != 0) {
    Nchan = FheadF.Ndouble;
    Format = FD_FLOAT64;
  }
  else {
    UTwarn ("AFrdEShead - %s", AFM_ES_UnsData);
    return NULL;
  }
  if (Fpreamb.Record_size != AF_DL[Format] * Nchan) {
    UTwarn ("AFrdEShead - %s", AFM_ES_UnsEncod);
    return NULL;
  }

/* Get the sampling frequency */
  Sfreq = AF_RecFreq (GenItems, N, Fbo);

/* Set the parameters for file access */
  if (FheadF.Ndrec == 0)
    Nsamp = AF_NSAMP_UNDEF;
  else
    Nsamp = FheadF.Ndrec * Nchan;
  AFp = AFsetRead (fp, FT_ESPS, Format, Fbo, Sfreq, 1.0, Nchan,
		   AF_LDATA_UNDEF, Nsamp, &Hinfo,
		   AF_FIX_NSAMP_LOW | AF_FIX_NSAMP_HIGH);

  return AFp;
}

/* Get the sampling frequency from the "record_freq" generic item */


static double
AF_RecFreq (GenItems, N, Fbo)

     const char GenItems[];
     int N;
     int Fbo;

{
  char *p;
  double8_t FSfreq;

  p = STstrstrNM (GenItems, RECORD_FREQ, N, SSIZE (RECORD_FREQ));
  if (p != NULL) {
    memcpy ((void *) &FSfreq, p + SSIZE (RECORD_FREQ), sizeof FSfreq);
    if (UTswapCode (Fbo) == DS_SWAP)
      SWAPB (FSfreq);
  }
  else {
    UTwarn ("AFrdEShead - %s", AFM_ES_NoSFreq);
    longjmp (AFR_JMPENV, 1);
  }

  return (double) FSfreq;
}
