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

Routine:
  AFILE *AFwrAUhead (FILE *fp, int Format, long int Nframe, long int Nchan,
                     double Sfreq)

Purpose:
  Write header information to an AU audio file

Description:
  This routine opens and writes header information to an AU audio file.  The
  additional header information returned by AFgenHinfo is written as part of
  the file header.

  AU audio file header:
      Bytes     Type    Contents
     0 ->  3    int    File identifier
     4 ->  7    int    Header size H (bytes)
     8 -> 11    int    Audio data length (bytes)
    12 -> 15    int    Data encoding format
    16 -> 19    int    Sample rate (samples per second)
    20 -> 23    int    Number of interleaved channels
    24 -> 27    int    AFsp identifier ("AFsp)")
    28 -> H-1   --     Additional header information
  8-bit mu-law, 8-bit A-law, 8-bit integer, 16-bit integer, 32-bit IEEE
  floating-point, and 64-bit IEEE floating-point data formats are supported.

Parameters:
  <-  AFILE AFwrAUhead
      Audio file pointer for the audio file.  In case of error, the audio file
      pointer is set to NULL.
   -> FILE *fp
      File pointer for the audio file
   -> int Format
      Audio file data format code,
        FD_MULAW8  = 1,  mu-law 8-bit data
        FD_ALAW8   = 2,  A-law 8-bit data
        FD_INT8    = 4,  two's-complement 8-bit integer data
        FD_INT16   = 5,  two's-complement 16-bit integer data
        FD_FLOAT32 = 6,  32-bit IEEE floating-point data
	FD_FLOAT64 = 7,  64-bit IEEE floating-point data
   -> long int Nframe
      Number of samples.  This value is used to set the file header for
      non-random access files.  If this value is AF_NFRAME_UNDEF, the number
      of samples is unknown.
   -> long int Nchan
      Number of channels
   -> double Sfreq
      Sampling frequency

Author / revision:
  P. Kabal  Copyright (C) 1998
  $Revision: 1.62 $  $Date: 1998/06/26 20:52:09 $

-------------------------------------------------------------------------*/

static char rcsid [] = "$Id: AFwrAUhead.c 1.62 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>
#define AF_DATA_LENGTHS
#include <libtsp/AFpar.h>
#include <libtsp/AUpar.h>

#define ICEILV(n,m)	(((n) + ((m) - 1)) / (m))	/* int n,m >= 0 */
#define RNDUPV(n,m)	((m) * ICEILV (n, m))		/* Round up */

#define MCOPY(src,dest)		memcpy ((void *) (dest), \
					(const void *) (src), sizeof (dest))
#define WRPAD(fp,size,align) \
     AFwriteHead (fp, NULL, 1, (int) (RNDUPV(size, align) - (size)), \
		  DS_NATIVE);

#define ALIGN		4	/* Header length is a multiple of ALIGN */

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


AFILE *
AFwrAUhead (fp, Format, Nframe, Nchan, Sfreq)

     FILE *fp;
     int Format;
     long int Nframe;
     long int Nchan;
     double Sfreq;

{
  struct AU_head Fhead;
  AFILE *AFp;
  int Lhead, Dencod;
  double ScaleF;
  struct AF_info *Hinfo;

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

/* Set up the encoding parameters */
  switch (Format) {
  case FD_MULAW8:
    Dencod = AU_MULAW8;
    ScaleF = 1./AU_SF_MULAW8;
    break;
  case FD_ALAW8:
    Dencod = AU_ALAW8;
    ScaleF = 1./AU_SF_ALAW8;
    break;
  case FD_INT8:
    Dencod = AU_LIN8;
    ScaleF = 1./AU_SF_LIN8;
    break;
  case FD_INT16:
    Dencod = AU_LIN16;
    ScaleF = 1./AU_SF_LIN16;
    break;
  case FD_FLOAT32:
    if (! UTcheckIEEE ())
      UTwarn ("AFwrAUhead - %s", AFM_AU_NoIEEE);
    Dencod = AU_FLOAT32;
    ScaleF = 1./AU_SF_FLOAT32;
    break;
  case FD_FLOAT64:
    if (! UTcheckIEEE ())
      UTwarn ("AFwrAUhead - %s", AFM_AU_NoIEEE);
    Dencod = AU_DOUBLE64;
    ScaleF = 1./AU_SF_DOUBLE64;
    break;
  default:
    UTwarn ("AFwrAUhead - %s", AFM_AU_UnsData);
    return NULL;
  }

/* Create the header information */
  /* Get the header information string */
  Hinfo = AFgenHinfo (Sfreq);
  Lhead = RNDUPV (AU_LHMIN + 4L + Hinfo->N, ALIGN);	/* Round up */

/* Set up the fixed header parameters */
  MCOPY (FM_AU, Fhead.Magic);
  Fhead.Lhead = Lhead;
  if (Nframe == AF_NFRAME_UNDEF)
    Fhead.Ldata = AU_NOSIZE;
  else
    Fhead.Ldata = Nframe * Nchan * AF_DL[Format];
  Fhead.Dencod = Dencod;
  Fhead.Srate = (uint4_t) (Sfreq + 0.5);	/* Rounding */
  Fhead.Nchan = (uint4_t) Nchan;
  MCOPY ("\0\0\0\0", Fhead.AFspID);

/* Write out the header */
  WHEAD_S (fp, Fhead.Magic);
  WHEAD_V (fp, Fhead.Lhead, DS_EB);
  WHEAD_V (fp, Fhead.Ldata, DS_EB);
  WHEAD_V (fp, Fhead.Dencod, DS_EB);
  WHEAD_V (fp, Fhead.Srate, DS_EB);
  WHEAD_V (fp, Fhead.Nchan, DS_EB);
  if (Hinfo->N == 0)
    WHEAD_S (fp, Fhead.AFspID);
  else {
    WHEAD_SN (fp, FM_AFSP, (sizeof FM_AFSP) - 1);	/* Omit null char */
    WHEAD_SN (fp, Hinfo->Info, Hinfo->N);
    WRPAD (fp, Hinfo->N, ALIGN);
  }

/* Set the parameters for file access */
  AFp = AFsetWrite (fp, FT_AU, Format, DS_EB, Sfreq, ScaleF, Nchan);

  return AFp;
}
