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

Routine:
  AFILE *AFrdSWhead (FILE *fp)

Purpose:
  Get file format information from a Comdisco SPW Signal file

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

  Comdisco SPW Signal file header:
   Offset Length Type    Contents
      0    12    char   File identifier ("$SIGNAL FILE 9\n")
  The remaining header consists of lines of text data, with each line
  terminated by a newline character.  The header is divided into sections with
  a section header marked by a string starting with a "$" character.  An
  example header is shown below
    $USER_COMMENT
    <comment line(s)>
    $COMMON_INFO
    SPW Version        = 3.10
    System Type        = <machine> (e.g. "sun4")
    Sampling Frequency = <Sfreq>   (e.g. "8000")
    Starting Time      = 0
    $DATA_INFO
    Number of points   = <Nsamp>   (e.g. "2000")
    Signal Type        = <type>    ("Double", "Float", "Fixedpoint")
    Fixed Point Format = <16.0,t>  (optional)
    Complex Format     = Real_Imag (optional)
    $DATA <data_type>              ("ASCII", "BINARY")
  8-bit integer, 16-bit integer, 32-bit floating-point, 64-point
  floating-point, and text formats are supported.  This routine does not
  support files with time stamps.

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

Author / revision:
  P. Kabal  Copyright (C) 1998
  $Revision: 1.24 $  $Date: 1998/06/18 16:08:38 $

-------------------------------------------------------------------------*/

static char rcsid [] = "$Id: AFrdSWhead.c 1.24 1998/06/18 libtsp-v3r0 $";

#include <string.h>

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

#define NCHEAD_MAX	1024
#define NCHEAD_MIN	(196 + 12)

/*
 Minimal header              Number of chars
$SIGNAL_FILE 9			14
$USER_COMMENT			13
				 0
$COMMON_INFO			12
SPW Version        = 3		22
System Type        = ?		22
Sampling Frequency = 1		22
Starting Time      = 0		22
$DATA_INFO			10
Number of points   = 1		22
Signal Type        = Float	26
$DATA ASCII			11

                   total       196  + 12 line ends
*/

#define SW_FD_INT32		128

#define SW_UNDEF		-1
#define SW_SIGNAL_FILE_9	0
#define SW_USER_COMMENT		1
#define SW_COMMON_INFO		2
#define SW_DATA_INFO		3
#define SW_DATA_ASCII		4
#define SW_DATA_BINARY		5
static const char *SW_SectTab[] = {
  "$SIGNAL_FILE 9",
  "$USER_COMMENT",
  "$COMMON_INFO",
  "$DATA_INFO",
  "$DATA* ASCII",	/* ASCII is optional */
  "$DATA BINARY",
  NULL
};
static const int SW_sect[] = {
  SW_SIGNAL_FILE_9,
  SW_USER_COMMENT,
  SW_COMMON_INFO,
  SW_DATA_INFO,
  SW_DATA_ASCII,
  SW_DATA_BINARY
};

static const char *SW_CITab[] = {
  "SPW Version",
  "System Type",
  "Sampling Frequency",
  "Starting Time",
  NULL
};
static const char *SW_DITab[] = {
  "Number of points",
  "Signal Type",
  "fixed point Format",
  "Complex Format",
  NULL
};

static int
AF_CommonInfo p_((char line[], int *Fbo, double *Sfreq));
static int
AF_DataInfo p_((char line[], long int *Nchan, long int *Nsamp, int *Format,
		double *ScaleF));


AFILE *
AFrdSWhead (fp)

     FILE *fp;

{
  AFILE *AFp;
  int Format, Fbo, sect, n, nc, ErrCode;
  long int Nsamp, Nchan;
  char *line;
  double ScaleF, Sfreq;
  char Info[AF_MAXINFO];
  struct AF_info Hinfo;

  /* Defaults */
  Nchan = 1;
  Nsamp = AF_NSAMP_UNDEF;
  Sfreq = -1.0;
  Format = FD_UNDEF;
  Fbo = DS_NATIVE;
  ScaleF = 1.0;

  ErrCode = 0;
  nc = 0;
  sect = SW_UNDEF;
  Hinfo.Info = Info;
  Hinfo.N = 0;

  while (sect != SW_DATA_ASCII && sect != SW_DATA_BINARY) {

    if (nc >= NCHEAD_MAX) {
      UTwarn ("AFrdSWhead - %s", AFM_SW_LongHead);
      return NULL;
    }

    /* Read a line from the header */
    line = AFgetLine (fp, &ErrCode);
    if (line == NULL && ! ErrCode) {
      UTwarn ("AFrdSWhead - %s", AFM_UEoF);
      return NULL;
    }
    nc += strlen (line);
    ++nc;	/* Account for newline */

    /* Compare with section headers */
    n = STkeyMatch (line, SW_SectTab);
    if (n >= 0) {
      sect = SW_sect[n];
    }
    else {
      switch (sect) {

      case SW_UNDEF:
	UTwarn ("AFrdSWhead - %s", AFM_SW_BadId);
	return NULL;

      case SW_SIGNAL_FILE_9:
	break;

      case SW_USER_COMMENT:
	if (strlen (line) > 0) {
	  strcpy (Hinfo.Info, "user_comment: ");
	  Hinfo.N = STcatMax (line, Hinfo.Info, AF_MAXINFO-1);
	}
	break;

      case SW_COMMON_INFO:
	if (AF_CommonInfo (line, &Fbo, &Sfreq))
	  return NULL;
	break;

      case SW_DATA_INFO:
	if (AF_DataInfo (line, &Nchan, &Nsamp, &Format, &ScaleF))
	  return NULL;
	break;

      default:
	UTwarn ("AFrdSWhead - %s: \"%.30s\"", AFM_SW_UnkKey, line);
	return NULL;
      }
    }
  }
  if (sect == SW_DATA_ASCII)
    Format = FD_TEXT;
    
/* Check for missing header information */
  if (Format == FD_UNDEF) {
    UTwarn ("AFrdSWhead - %s", AFM_SW_NoData);
    return NULL;
  }
  if (Sfreq <= 0.0) {
    UTwarn ("AFrdSWhead - %s", AFM_SW_NoSFreq);
    return NULL;
  }

/* Check for incompatible options */
  if (Format == FD_TEXT && Nchan == 2) {
    UTwarn ("AFrdSWhead - %s", AFM_SW_AscCmplx);
    return NULL;
  }
  if (Format == SW_FD_INT32) {
    UTwarn ("AFrdSWhead %s", AFM_SW_IntBin);
    return NULL;
  }

/* Set the parameters for file access */
  AFp = AFsetRead (fp, FT_SPW, Format, Fbo, Sfreq, ScaleF, Nchan,
		   AF_LDATA_UNDEF, Nsamp, &Hinfo, AF_NOFIX);

  return AFp;
}

/* Decode common info */


static int
AF_CommonInfo (line, Fbo, Sfreq)

     char line[];
     int *Fbo;
     double *Sfreq;

{
  int n;
  float STime;

  n = STkeyXpar (line, "=", "", SW_CITab, line);
  switch (n) {
  case 0:
    /* SPW Version - ignore */
    break;
  case 1:
    /* System Type */
    if (strcmp (line, "sun4") == 0 || strcmp (line, "hp700") == 0)
      *Fbo = DS_EB;
    else {
      *Fbo = DS_NATIVE;
      UTwarn ("AFrdSWhead - %s: \"%.30s\"", AFM_SW_UnkSys, line);
    }
    break;
  case 2:
    /* Sampling Frequency */
    if (STdec1double (line, Sfreq) || *Sfreq <= 0.0) {
      UTwarn ("AFrdSWhead - %s: \"%.30s\"", AFM_SW_BadSFreq, line);
      return 1;
    }
    break;
  case 3:
    /* Starting Time */
    if (STdec1float (line, &STime)) {
      UTwarn ("AFrdSWhead - %s: \"%.30s\"", AFM_SW_BadSTime, line);
      return 1;
    }
    if (STime != 0.0)
      UTwarn ("AFrdSWhead - %s: %g\n", AFM_SW_NZSTime, STime);
    break;
  default:
    UTwarn ("AFrdSWhead - %s: \"%.30s\"", AFM_SW_UnkCOM, line);
    break;
  }

  return 0;
}

/* Decode data info */


static int
AF_DataInfo (line, Nchan, Nsamp, Format, ScaleF)

     char line[];
     long int *Nchan;
     long int *Nsamp;
     int *Format;
     double *ScaleF;

{
  int n;

  n = STkeyXpar (line, "=", "", SW_DITab, line);
  switch (n) {
  case 0:
    /* Number of points */
    if (STdec1long (line, Nsamp) || *Nsamp <= 0) {
      UTwarn ("AFrdSWhead: %s: \"%.30s\"", AFM_SW_BadNSamp, line);
      return 1;
    }
    break;
  case 1:
    /* Signal Type */
    if (strcmp (line, "Double") == 0) {
      *Format = FD_FLOAT64;
      *ScaleF = 32768.;
    }
    else if (strcmp (line, "Float") == 0) {
      *Format = FD_FLOAT32;
      *ScaleF = 32768.;
    }
    else if (strcmp (line, "Fixed-point") == 0) {
      *Format = FD_INT16;
      *ScaleF = 1.;
    }
    else if (strcmp (line, "Integer") == 0) {
      *Format = SW_FD_INT32;
      *ScaleF = 1.;
    }
    else {
      UTwarn ("AFrdSWhead - %s: \"%.30s\"", AFM_SW_UnsData, line);
      return NULL;
    }
    break;
  case 2:
    /* fixed point Format */
    if (strcmp (line, "<16,16,t>") == 0) {
      *Format = FD_INT16;
      *ScaleF = 1.;
    }
    if (strcmp (line, "<16,0,t>") == 0) {
      *Format = FD_INT16;
      *ScaleF = 32768;
    }
    else if (strcmp (line, "<8,8,t>") == 0) {
      *Format = FD_INT8;
      *ScaleF = 128.;
    }
    else {
      UTwarn ("AFrdSWhead - %s: \"%.30s\"", AFM_SW_UnsFixP, line);
      return NULL;
    }
    break;
  case 3:
    /* Complex Format */
    if (strcmp (line, "Real_Imag") == 0)
      *Nchan = 2;
    else {
      UTwarn ("AFrdSWhead - %s: \"%.30s\"", AFM_SW_UnsCmplx, line);
      return 1;
    }
    break;
  default:
    UTwarn ("AFrdSWhead - %s: \"%.30s\"", AFM_SW_UnsDInfo, line);
    return 1;
  }

  return 0;
}
