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

Routine:
  AFILE *AFrdBLhead (FILE *fp)

Purpose:
  Get file format information from an SPPACK file

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

  SPPACK sampled data file:
   Offset Length Type    Contents
      0   160    char   Text strings (2 * 80)
    160    80    char   Command line
    240     2    int    Domain
    242     2    int    Frame size
    244     4    float  Sampling frequency
    252     2    int    File identifier
    254     2    int    Data type
    256     2    int    Resolution
    258     2    int    Companding
    260   240    char   Text strings (3 * 80)
    500     6    int    Datlink information (3 * 2)
    512   ...    --     Audio data
  8-bit mu-law, 8-bit A-law, and 16-bit integer data formats are supported.

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

Author / revision:
  P. Kabal  Copyright (C) 1998
  $Revision: 1.40 $  $Date: 1998/06/26 20:33:02 $

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

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

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

#define SAME_CSTR(str,ref) 	(memcmp (str, ref, sizeof (str)) == 0)

#define LHEAD		512L
#define FM_SPPACK	"\100\303"	/* Magic value in file byte order */

#define S_SAMPLEDDATA	((uint2_t) 0xfc0e)	/* sampled data file */
#define DLMAGIC		4567

enum {
  C_UNIFORM = 1,	/* uniform */
  C_ALAW = 2,		/* A-law companding */
  C_MULAW = 3		/* mu-law companding */
};
enum {
  D_TIME = 1,		/* time domain */
  D_FREQ = 2,		/* frequency domain */
  D_QUEFR = 3		/* quefrency domain */
};

struct SPPACK_head {
/*  char cs1[80]; */
/*  char cs2[80]; */
/*  char cmd[80]; */
  uint2_t Domain;	/* Domain */
/*  uint2_t FrameSize; */
  float4_t Sfreq;	/* Sampling frequency */
/*  uint2_t unused[2]; */
  char Magic[2];	/* File magic */
  uint2_t Dtype;	/* Data type */
  uint2_t Resolution;	/* Resolution in bits */
  uint2_t Compand;	/* Companding flag */
/*  char lstr1[80]; */
/*  char lstr2[80]; */
/*  char lstr3[80]; */
  uint2_t dl_magic;	/* Datlink magic */
  uint2_t left;		/* Left channel flag */
  uint2_t right;	/* Right channel flag */
  /* uint2_t unused2[2]; */
};

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


AFILE *
AFrdBLhead (fp)

     FILE *fp;

{
  AFILE *AFp;
  int Lb, Format;
  int offs, Nchan;
  char Info[AF_MAXINFO];
  struct AF_info Hinfo;
  struct SPPACK_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 the header parameters */
  Hinfo.Info = Info;
  Hinfo.N = 0;
  offs = AFrdHtext (fp, 80, "cs1: ", &Hinfo, 1);
  offs += AFrdHtext (fp, 80, "cs2: ", &Hinfo, 1);
  offs += AFrdHtext (fp, 80, "cmd: ", &Hinfo, 1);
  offs += RHEAD_V (fp, Fhead.Domain, DS_EB);
  offs += RSKIP (fp, 2);	/* skip FrameSize */
  offs += RHEAD_V (fp, Fhead.Sfreq, DS_EB);
  offs += RSKIP (fp, 4);	/* skip two fill values */
  offs += RHEAD_S (fp, Fhead.Magic);
  offs += RHEAD_V (fp, Fhead.Dtype, DS_EB);
  offs += RHEAD_V (fp, Fhead.Resolution, DS_EB);
  offs += RHEAD_V (fp, Fhead.Compand, DS_EB);
  offs += RSKIP (fp, 240);
  offs += RHEAD_V (fp, Fhead.dl_magic, DS_EB);
  offs += RHEAD_V (fp, Fhead.left, DS_EB);
  offs += RHEAD_V (fp, Fhead.right, DS_EB);
  RSKIP (fp, (LHEAD - offs));

  if (! SAME_CSTR (Fhead.Magic, FM_SPPACK)) {
    UTwarn ("AFrdBLhead - %s", AFM_BL_BadId);
    return NULL;
  }

/* Set up the data format parameters */
  if (Fhead.Dtype == S_SAMPLEDDATA) {
    switch (Fhead.Compand) {
    case C_MULAW:
      Lb = 8;
      Format = FD_MULAW8;
      break;
    case C_ALAW:
      Lb = 8;
      Format = FD_ALAW8;
      break;
    case C_UNIFORM:
      Lb = 16;
      Format = FD_INT16;
      break;
    default:
      UTwarn ("AFrdBLhead - %s: \"%d\"", AFM_BL_UnsComp, (int) Fhead.Compand);
      return NULL;
    }
  }
  else {
    UTwarn ("AFrdBLhead - %s: \"%d\"", AFM_BL_UnsData, (int) Fhead.Dtype);
    return NULL;
  }

  /* Error checks */
  if (Fhead.Resolution != Lb) {
    UTwarn ("AFrdBLhead - %s: \"%d\"", AFM_BL_UnsWLen, (int) Fhead.Resolution);
    return NULL;
  }
  if (Fhead.Domain != D_TIME) {
    UTwarn ("AFrdBLhead - %s: \"%d\"", AFM_BL_UnsDomain, (int) Fhead.Domain);
    return NULL;
  }
  Nchan = 1L;
  if (Fhead.dl_magic == DLMAGIC) {
    Nchan = 0L;
    if (Fhead.right)
      ++Nchan;
    else if (Fhead.left)
      ++Nchan;
  }

/* Set the parameters for file access */
  AFp = AFsetRead (fp, FT_SPPACK, Format, DS_EB, (double) Fhead.Sfreq, 1.0,
		   Nchan, AF_LDATA_UNDEF, AF_NSAMP_UNDEF, &Hinfo, AF_NOFIX);

  return AFp;
}
