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

Routine:
  AFILE *AFopenWrite (const char Fname[], int Fformat, long int Nchan,
                      double Sfreq, FILE *fpinfo)

Purpose:
  Open an audio file for writing

Description:
  This routine opens an audio file for writing.  This routine sets up the audio
  file parameters to write data of the given format to the audio file.  After
  writing data to the file, the routine AFclose should be called to update the
  file header information and close the file.

  By default, information consisting of the date, the user and the program
  creating the file is written to the audio file header.  The routine
  AFsetHInfo can be called before calling this routine to specify additional
  information to be written to the header.  Any such additional header
  information is reset to null after being written to the header.
 
  This routine can write AU audio files, AIFF-C files, RIFF WAVE files, and
  headerless audio files.

  AU audio file:
   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.
  RIFF WAVE file:
    8-bit mu-law, 8-bit A-law, offset-binary 8-bit integer, and 16-bit integer
    data formats are supported.
  AIFF-C file:
    8-bit mu-law, 8-bit A-law, 8-bit integer, and 16-bit integer data formats
    are supported.
  Headerless file:
    8-bit mu-law, 8-bit A-law, offset-binary 8-bit integer, 8-bit integer,
    16-bit integer, 32-bit floating-point, 64-bit floating-point, and text data
    formats are supported.  A text format file has the data in character form,
    one value to a line.

  For the fixed point file data representations, input values in the following
  ranges will be converted without clipping.
     data format       allowed values
    8-bit mu-law   - [ -32636, +32636 ]
    8-bit A-law    - [ -32768, +32768 ]
    8-bit integer  - [ -16384, -16383 ]
   16-bit integer - [ -32768, +32767 ]
  For AU files containing floating-point data, values are scaled by 1/32768
  before being written to the file.  For data values between [-32768, +32768],
  the file data will lie in the range [-1, +1].  For headerless files of any
  data format, the values are written scaled by unity.

Parameters:
  <-  AFILE *AFopenWrite
      Audio file pointer for the audio file
   -> const char Fname[]
      Character string specifying the file name.  The file name "-" means
      standard output.
   -> int Fformat
      Audio file format code, evaluated as the sum of a data format code and a
      file type,
        Fformat = Dformat + 256 * Ftype
      For Ftype equal to zero, the file will have the standard AU audio file
      header.  The Ftype flag allows for other file types as described below.
      Note that not all data formats are allowed for all file types.
      Dformat: data format
        FD_MULAW8  = 1,  mu-law 8-bit data
        FD_ALAW8   = 2,  A-law 8-bit data
        FD_UINT8   = 3,  offset-binary 8-bit integer 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 floating-point data
        FD_FLOAT64 = 7,  64-bit floating-point data
        FD_TEXT    = 8,  text data
      Ftype: file type
        FTW_AU        = 0, AU audio file
        FTW_WAVE      = 1, RIFF WAVE file
        FTW_AIFF_C    = 2, AIFF-C audio file
        FTW_NH_EB     = 3, Headerless file (big-endian byte order)
        FTW_NH_EL     = 4, Headerless file (little-endian byte order)
        FTW_NH_NATIVE = 5, Headerless file (native byte order)
        FTW_NH_SWAP   = 6, Headerless file (byte-swapped byte order)
   -> long int Nchan
      Number of channels
   -> double Sfreq
      Sampling frequency
   -> FILE *fpinfo
      File pointer for printing audio file information.  If fpinfo is not NULL,
      information about the audio file is printed on the stream selected by
      fpinfo.

Author / revision:
  P. Kabal  Copyright (C) 1998
  $Revision: 1.59 $  $Date: 1998/06/26 13:34:10 $

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

#include <stdio.h>
#include <string.h>

#include <libtsp.h>
#include <libtsp/AFheader.h>
#include <libtsp/Xstdio.h>
#include <libtsp/Xstdlib.h>
#include <libtsp/AFmsg.h>
#define AF_DATA_TYPE_NAMES
#include <libtsp/AFpar.h>
#include <libtsp/sysOS.h>

static int
AF_checkDT p_((int Ftype, int Dformat));
static int
AF_checkPar p_((int Ftype, int Dformat, long int Nframe, long int Nchan));
static void
AF_error p_((const char Fname[], int sysFlag));
static void
AF_resetPar p_((void));

/* If AFopts_ErrorHalt is clear, execution continues after an error */


AFILE *
AFopenWrite (Fname, Fformat, Nchan, Sfreq, fpinfo)

     const char Fname[];
     int Fformat;
     long int Nchan;
     double Sfreq;
     FILE *fpinfo;

{
  AFILE *AFp;
  FILE *fp;
  int Dformat, Ftype;
  long int Nframe;
  const char *mode;

/* Separate the file type and data type */
  Dformat = FW_dformat (Fformat);
  Ftype = FW_ftype (Fformat);

/* Check the input parameters */
  Nframe = AFopt_Nframe;
  if (AF_checkPar (Ftype, Dformat, Nframe, Nchan)) {
    AF_error (Fname, 0);
    return NULL;
  }

/* Open the file for writing */
  mode = "wb";
  /* Open the file as a text file if necessary */
  /* For Unix text files and binary files are the same */
#ifndef unix
  if ((Ftype == FTW_NH_EB || Ftype == FTW_NH_EL ||
       Ftype == FTW_NH_NATIVE || Ftype == FTW_NH_SWAP) &&
      AFopt_NHpar.Format == FD_TEXT)
    mode = "w";
#endif
  if (strcmp (Fname, "-") == 0)
    fp = stdout;
  else
    fp = fopen (Fname, mode);
  if (fp == NULL) {
    AF_error (Fname, 1);
    return NULL;
  }

/* Set up the audio file parameters and write the header information */
  switch (Ftype) {
  case FTW_AU:
    AFp = AFwrAUhead (fp, Dformat, Nframe, Nchan, Sfreq);
    break;
  case FTW_WAVE:
    AFp = AFwrWVhead (fp, Dformat, Nframe, Nchan, Sfreq);
    break;
  case FTW_AIFF_C:
    AFp = AFwrAIhead (fp, Dformat, Nframe, Nchan, Sfreq);
    break;
  case FTW_NH_EB:
  case FTW_NH_EL:
  case FTW_NH_NATIVE:
  case FTW_NH_SWAP:
    AFp = AFsetNHwrite (fp, Ftype, Dformat, Nchan, Sfreq);
    break;
  default:
    AFp = NULL;
    break;
  }

/* Error messages */
  if (AFp == NULL) {
    fclose (fp);
    AF_error (Fname, 0);
    return NULL;
  }

/* Reset open parameters */
  AF_resetPar ();

/* Print the header information */
  AFprAFpar (AFp, Fname, fpinfo);

return AFp;
}

/* Check the user supplied parameters */


static int
AF_checkPar (Ftype, Dformat, Nframe, Nchan)

     int Ftype;
     int Dformat;
     long int Nframe;
     long int Nchan;

{
  int ErrCode;

  /* Check the file type / data format combination */
  ErrCode = AF_checkDT (Ftype, Dformat);

  if (Nchan <= 0L) {
    UTwarn ("AFopenWrite - %s: %ld", AFM_BadNChan, Nchan);
    ErrCode = 1;
  }
  if (Nframe != AF_NFRAME_UNDEF && Nframe < 0L) {
    UTwarn ("AFopenWrite - %s: %ld", AFM_BadNFrame, Nframe);
    ErrCode = 1;
  }

  return ErrCode;
}

/* Check for file type / data type compatibility */


static int
AF_checkDT (Ftype, Dformat)

     int Ftype;
     int Dformat;

{
  int ErrCode;

/*	undef	mu-law	A-law	uint8	int8	int16	float32	float64	text */
  static const int DT_AU[NFD] = {
	0,	1,	1,	0,	1,	1,	1,	1,	0};
  static const int DT_WAVE[NFD] = {
	0,	1,	1,	1,	0,	1,	0,	0,	0};
  static const int DT_AIFF_C[NFD] = {
	0,	1,	1,	0,	1,	1,	0,	0,	0};

  ErrCode = 0;
  if (Dformat < 1 || Dformat >= NFD) {
    UTwarn ("AFopenWrite - %s: \"%d\"", AFM_BadDataC, Dformat);
    ErrCode = 1;
    return ErrCode;
  }

  switch (Ftype) {
  case FTW_AU:
    if (DT_AU[Dformat] == 0) {
      UTwarn ("AFopenWrite - %s: \"%s\"", AFM_AU_UnsData, AF_DTN[Dformat]);
      ErrCode = 1;
    }
    break;
  case FTW_WAVE:
    if (DT_WAVE[Dformat] == 0) {
      UTwarn ("AFopenWrite - %s: \"%s\"", AFM_WV_UnsData, AF_DTN[Dformat]);
      ErrCode = 1;
    }
    break;
  case FTW_AIFF_C:
    if (DT_AIFF_C[Dformat] == 0) {
      UTwarn ("AFopenWrite - %s: \"%s\"", AFM_AIFF_UnsData, AF_DTN[Dformat]);
      ErrCode = 1;
    }
    break;
  case FTW_NH_EB:
  case FTW_NH_EL:
  case FTW_NH_NATIVE:
  case FTW_NH_SWAP:
    break;		/* All data types supported */
  default:
    UTwarn ("AFopenWrite - %s: %d", AFM_BadFTypeC, Ftype);
    ErrCode = 1;
    break;
  }

  return ErrCode;
}

/* Print an error message with the file name */


static void
AF_error (Fname, sysFlag)

     const char Fname[];
     int sysFlag;

{
  const char *fn;

  if (strcmp (Fname, "-") == 0)
    fn = "<stdout>";
  else
    fn = Fname;

  if (sysFlag)
    UTsysMsg ("AFopenWrite: %s \"%s\"", AFM_OpenWErr, fn);
  else
    UTwarn ("AFopenWrite: %s \"%s\"", AFM_OpenWErr, fn);
  if (AFopt_ErrorHalt)
    exit (EXIT_FAILURE);

  /* Reset open parameters */
  AF_resetPar ();

  return;
}

/* Reset per file parmeters */


static void
AF_resetPar ()

{
  AFopt_Nframe = AF_NFRAME_UNDEF;
  AFsetHinfo (NULL);

  return;
}
