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

Routine:
  AFILE *AFrdSFhead (FILE *fp)

Purpose:
  Get file format information from an IRCAM soundfile

Description:
  This routine reads the header for an IRCAM soundfile.  The header information
  is used to set the file data format information in the audio file pointer
  structure.  A banner identifying the audio file and its parameters is
  printed.

  IRCAM soundfile header:
   Offset Length Type    Contents
      0     4    int    File identifier
      4     4    float  Sampling frequency
      8     4    int    Number of interleaved channels
     12     4    int    Data type
     16    ...   --     Additional header information
   1024    ...   --     Audio data
  8-bit mu-law, 8-bit A-law, 8-bit integer, 16-bit integer, and 32-bit
  floating-point data formats are supported.

Parameters:
  <-  AFILE *AFrdSFhead
      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:38:31 $

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

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

#include <libtsp.h>
#include <libtsp/nucleus.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		1024
#define LHMIN		((int) sizeof (struct SF_head))
/* Magic in file byte order */
/* - two byte magic, 1 byte machine type, zero byte */
#define FM_SF_VAX_L	"\144\243\001\0"	/* vax native data */
#define FM_SF_VAX_B	"\0\001\243\144"	/* vax - byte reversed data */
#define FM_SF_SUN	"\144\243\002\0"	/* big-endian data*/
#define FM_SF_MIPS_L	"\144\243\003\0"	/* little-endian data */
#define FM_SF_MIPS_B	"\0\003\243\144"	/* big-endian data */
#define FM_SF_NEXT	"\144\243\004\0"	/* big-endian data */

#define SF_CHAR		0x00001
#define SF_ALAW		0x10001
#define SF_ULAW		0x20001
#define SF_SHORT	0x00002
#define SF_LONG		0x40004
#define SF_FLOAT	0x00004

#define SF_END		0
#define SF_MAXAMP	1
#define SF_COMMENT	2

struct SF_head {
  char Magic[4];	/* File magic */
  float4_t sf_srate;	/* Sampling frequency */
  uint4_t sf_chans;	/* Number of channels */
  uint4_t sf_packmode;	/* Encoding type */
};
struct SF_code {
  uint2_t code;
  uint2_t bsize;
};

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

static int
AF_getComment p_((FILE *fp, int Size, int Fbo, struct AF_info *Hinfo));


AFILE *
AFrdSFhead (fp)

     FILE *fp;

{
  AFILE *AFp;
  long int offs;
  int fileIEEE, Fbo, Format;
  double ScaleF;
  char Info[AF_MAXINFO];
  struct AF_info Hinfo;
  struct SF_head Fhead;

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

/* Check the file magic */
  offs = RHEAD_S (fp, Fhead.Magic);

  fileIEEE = 1;
  if (SAME_CSTR (Fhead.Magic, FM_SF_VAX_L)) {
    fileIEEE = 0;
    Fbo = DS_EL;
  }
  else if (SAME_CSTR (Fhead.Magic, FM_SF_MIPS_L))
    Fbo = DS_EL;
  else if (SAME_CSTR (Fhead.Magic, FM_SF_VAX_B) ||
	   SAME_CSTR (Fhead.Magic, FM_SF_SUN) ||
	   SAME_CSTR (Fhead.Magic, FM_SF_MIPS_B) ||
	   SAME_CSTR (Fhead.Magic, FM_SF_NEXT))
    Fbo = DS_EB;
  else {
    UTwarn ("AFrdSFhead - %s", AFM_SF_BadId);
    return NULL;
  }

/* Notes:
   - IRCAM files can be written by a number of different hosts.  The data is
     normally written out in native format (byte order and float format).
   - Early IRCAM software ran on VAX computers (little-endian) with float data
     (sampling frequency) in VAX float format.
   - Subsequent IRCAM software ran on Sun computers (big-endian).  The file
     data including the header values is byte-swapped with respect to the
     native VAX format.  The float format is IEEE.
   - The file magic for the VAX files is "\144\243\001\0".  The file magic for
     the corresponding Sun files is the byte reverse of this.
   - The MIT csound, the Princeton cmix and earlier sox systems support files
     as described above.
   - Subsequently, the magic in IRCAM files was been updated to indicate the
     host type used to create the file.  In file byte order the magic values
     and the corresponding host type are:
        "\144\243\001\0"   vax   little-endian data
        "\144\243\002\0"   sun   big-endian data
        "\144\243\003\0"   mips  little-endian data
        "\144\243\004\0"   next  big-endian data
   - The sox utility (as of version 11) has a compile time flag.  In the
     IRCAM mode (default), it writes files with either of two magic values
     depending on whether the host is big- or little-endian (shown in file
     byte order),
        "\144\243\001\0"   little-endian
        "\144\243\002\0"   big-endian
     In the non-IRCAM mode, the file magic is one of 4 values depending on
     the machine type (shown in file byte order),
        "\144\243\001\0"   vax   little-endian data
        "\144\243\002\0"   sun   big-endian data
        "\144\243\003\0"   mips  little-endian data
        "\144\243\004\0"   next  big-endian data
     The choice of machine is on the basis of the definition of the
     pre-processor symbols "vax", "sun", "mips" or "NeXT".  The mips choice
     is predicated on the machine being little-endian (DEC mips machines).
     However, SGI mips machines also set the mips symbol, but are big-endian
     and so will generate a byte swapped version of a "mips" file.
   - There are incompatibilities between the IRCAM data format codes and those
     used by the MIT Media lab csound package.  For instance, the csound
     program uses SF_ULAW as 0x00001.
   - There are examples of IRCAM files (bicsf files) with float data which
     have been scaled to +/-1.  We use a scale factor of 32768 for float data.

  For this implementation we accept IRCAM files as shown in the table below
  (magic values in file byte order).
       magic value   machine code  data byte-order  float-type
    "\144\243\001\0"  vax          big-endian        vax ?
    "\0\001\243\144"  vax (sun)    little-endian     IEEE
    "\144\243\002\0"  sun          big-endian        IEEE
    "\144\243\003\0"  mips (DEC)   little-endian     IEEE
    "\0\003\243\144"  mips (SGI)   big-endian        IEEE
    "\144\243\004\0"  next         big-endian        IEEE
*/

/* Read in the rest of the header */
  offs += RHEAD_V (fp, Fhead.sf_srate, Fbo);
  offs += RHEAD_V (fp, Fhead.sf_chans, Fbo);
  offs += RHEAD_V (fp, Fhead.sf_packmode, Fbo);

/* Pick up comments */
  Hinfo.Info = Info;
  Hinfo.N = 0;
  offs += AF_getComment (fp, (int) (LHEAD - offs), Fbo, &Hinfo);

/* Position at the start of data */
  RSKIP (fp, LHEAD - offs);

/* Set up the decoding parameters */
  switch (Fhead.sf_packmode) {
  case SF_CHAR:
    Format = FD_INT8;
    ScaleF = 128.;
    break;
  case SF_ULAW:
    Format = FD_MULAW8;
    ScaleF = 1.;
    break;
  case SF_ALAW:
    Format = FD_ALAW8;
    ScaleF = 1.;
    break;
  case SF_SHORT:
    Format = FD_INT16;
    ScaleF = 1.;
    break;
  case SF_FLOAT:
    Format = FD_FLOAT32;
    ScaleF = 32768.;
    break;
  default:
    UTwarn ("AFrdSFhead - %s: \"%ld\"", AFM_SF_UnsData, Fhead.sf_packmode);
    return NULL;
  }

/* Error checks */
  if (fileIEEE != UTcheckIEEE ()) {
    UTwarn ("AFrdSFhead - %s", AFM_SF_MisFloat);
    return NULL;
  }

/* Set the parameters for file access */
  AFp = AFsetRead (fp, FT_SF, Format, Fbo, (double) Fhead.sf_srate, ScaleF,
		   (long int) Fhead.sf_chans, AF_LDATA_UNDEF, AF_NSAMP_UNDEF,
		   &Hinfo, AF_NOFIX);

  return AFp;
}

/* Pick up comments and place them in the AFsp information structure */


static int
AF_getComment (fp, Size, Fbo, Hinfo)

     FILE *fp;
     int Size;
     int Fbo;
     struct AF_info *Hinfo;

{
  int offs, nc;
  struct SF_code SFcode;

  offs = 0;
  while (offs < Size) {
    offs += RHEAD_V (fp, SFcode.code, Fbo);
    if (SFcode.code == SF_END)
      break;
    offs += RHEAD_V (fp, SFcode.bsize, Fbo);
    nc = SFcode.bsize - (sizeof SFcode.code) - (sizeof SFcode.bsize);
    if (SFcode.code == SF_COMMENT)
      offs += AFrdHtext (fp, nc, "IRCAM comment: ", Hinfo, 1);
    else
      offs += RSKIP (fp, nc);
  }

  return offs;
}
