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

Routine:
  int AFfileType (FILE *fp)

Purpose:
  Identify the format of an audio file

Description:
  This routine determines the format for an audio file.  This routine checks
  the header of the file to identify the type of file.  This check for most
  file types consists of a check for a file magic value in a known location in
  the header of the file.  If the file has a header which is recognized, the
  file is declared to be a file of the corresponding type.

  The routine AFsetNH can be used to control the behaviour of this routine for
  audio files which do not have a header (raw audio files) or which have
  non-standard headers.  AFsetNH allows for three ways of handling such files.
  1: Files can be unconditionally accepted without checking the header
  2: Files are accepted as "headerless", but only after checking the header
     against the standard file formats
  3: Unrecognized files are not accepted

Parameters:
  <-  int AFfileType
      Integer code specifying the type of file,
        FT_UNKNOWN = 0, unknown audio file format
        FT_NH      = 1, headerless (non-standard or no header) audio file
        FT_AFSP    = 2, AFsp audio file
        FT_SUN     = 3, Sun audio file
        FT_WAVE    = 4, RIFF WAVE file
        FT_AIFF_C  = 5, AIFF-C audio file
        FT_SPHERE  = 6, NIST SPHERE audio file
        FT_ESPS    = 7, ESPS sampled data feature file
        FT_SF      = 8, IRCAM soundfile
        FT_SPPACK  = 9, SPPACK file
        FT_INRS    = 10, INRS-Telecom audio file
        FT_AIFF    = 11, AIFF audio file
        FT_TXAUD   = 12, Text audio file
   -> FILE *fp
      File pointer associated with the audio file

Author / revision:
  P. Kabal  Copyright (C) 1996
  $Revision: 1.43 $  $Date: 1996/08/19 13:38:07 $

-------------------------------------------------------------------------*/

static char rcsid[] = "$Id: AFfileType.c 1.43 1996/08/19 AFsp-V2R1 $";

#include <stdio.h>
#include <string.h>
#include <libtsp/nucleus.h>
#include <libtsp/AFpar.h>

/* File magic values in file byte order */
#define FM_SUN		"\056\163\156\144"	/* ".snd" */
#  define FM_AFSP	"\101\106\163\160"	/* "AFsp" */
#define FM_RIFF		"\122\111\106\106"	/* "RIFF" */
#  define FM_WAVE	"\127\101\126\105"	/* "WAVE" */
#define FM_IFF		"\106\117\122\115"	/* "FORM" */
#  define FM_AIFF	"\101\111\106\106"	/* "AIFF" */
#  define FM_AIFF_C	"\101\111\106\103"	/* "AIFC" */
#define FM_SPHERE	"\116\111\123\124\137\061\101\012"   /* "NIST_1A\n" */
#define FM_ESPS		"\0\0\152\32"
#define FM_ESPS_SWAPPED	"\32\152\0\0"
#define FM_SF_VAX_L	"\144\243\001\0"	/* VAX - native */
#define FM_SF_VAX_B	"\0\001\243\144"	/* VAX - byte reversed */
#define FM_SF_SUN	"\144\243\002\0"	/* big-endian */
#define FM_SF_MIPS_L	"\144\243\003\0"	/* big-endian */
#define FM_SF_MIPS_B	"\0\003\243\144"	/* little-endian */
#define FM_SF_NEXT	"\144\243\004\0"	/* big-endian */
#define FM_SPPACK	"\100\303"
#define FM_TXAUD	"\045\057\057"		/* "%//" */

/* Common sampling frequencies (VAX floating-point values) in file byte order.
   The sampling frequencies recognized are 6500, 20000/3, 8000, 10000, 12000,
   16000, and 20000 Hz. */
#define NINRS	7
static const char *FM_INRS[NINRS] = {
  "\313\106\0\040",   "\320\106\125\125", "\372\106\0\0",   "\034\107\0\100",
  "\073\107\0\200",   "\172\107\0\0",     "\234\107\0\100"
};

/* Offset to the file magic */
#define MOFFS_SUN	0
#  define MOFFS_AFSP	24
#define MOFFS_RIFF	0
#  define MOFFS_WAVE	8
#define MOFFS_IFF	0
#  define MOFFS_AIFF	8
#  define MOFFS_AIFF_C	MOFF_AIFF
#define MOFFS_SPHERE	0
#define MOFFS_ESPS	16
#define MOFFS_SF	0
#define MOFFS_SPPACK	252
#define MOFFS_INRS	0
#define MOFFS_TXAUD	0

/* Size of the file magic */
#define MSIZE_SUN	(sizeof (FM_SUN) - 1)
#  define MSIZE_AFSP	(sizeof (FM_AFSP) - 1)
#define MSIZE_RIFF	(sizeof (FM_RIFF) - 1)
#  define MSIZE_WAVE	(sizeof (FM_WAVE) - 1)
#define MSIZE_SPHERE	(sizeof (FM_SPHERE) - 1)
#define MSIZE_SF	(sizeof (FM_SF_SUN) - 1)
#define MSIZE_ESPS	(sizeof (FM_ESPS) - 1)
#define MSIZE_SPPACK	(sizeof (FM_SPPACK) - 1)
#define MSIZE_INRS	(sizeof (FM_INRS[0]) - 1)
#define MSIZE_IFF	(sizeof (FM_IFF) - 1)
#  define MSIZE_AIFF	(sizeof (FM_AIFF) - 1)
#  define MSIZE_AIFF_C	(sizeof (FM_AIFF_C) - 1)
#define MSIZE_TXAUD	(sizeof (FM_TXAUD) - 1)

/* Minimum header lengths */
#define LHMIN_SUN	24
#  define LHMIN_AFSP	(LHMIN_SUN + 4)
#define LHMIN_RIFF	16
#  define LHMIN_WAVE	44
#define LHMIN_SPHERE	1024
#define LHMIN_SF	1024
#define LHMIN_ESPS	333
#define LHMIN_SPPACK	512
#define LHMIN_INRS	512
#define LHMIN_IFF	16
#  define LHMIN_AIFF	54
#  define LHMIN_AIFF_C	72
#define LHMIN_TXAUD	21

#define RHEAD_SN(fp,offs,string,N) \
			AFreadHead (fp, (long int) (offs), (void *) string, \
				    1, (int) (N), DS_NATIVE)
#define ID_CSTR(str,ref,len) 	(memcmp (str, ref, (size_t) len) == 0)

int
AFfileType (fp)

     FILE *fp;

{
  char Magic[8];		/* Longest Magic is 8 bytes */
  long int Nbytes;
  int i;
  struct AF_NHpar *Fdef;

/* Check for a forced headerless file */
  Fdef = AFgetNH ();
  if (Fdef->Accept == NH_FORCE)
    return FT_NH;

/* Get the file size */
  Nbytes = FLfileSize (fp);

/* Check for an AFsp or Sun audio file */
  if (Nbytes >= LHMIN_SUN) {
    RHEAD_SN (fp, MOFFS_SUN, Magic, MSIZE_SUN);
    if (ID_CSTR (Magic, FM_SUN, MSIZE_SUN)) {
      if (Nbytes >= LHMIN_AFSP) {
	RHEAD_SN (fp, MOFFS_AFSP, Magic, MSIZE_AFSP);
	if (ID_CSTR (Magic, FM_AFSP, MSIZE_AFSP))
	  return FT_AFSP;
      }
      return FT_SUN;
    }
  }

/* Check for a RIFF WAVE file */
  if (Nbytes >= LHMIN_RIFF) {
    RHEAD_SN (fp, MOFFS_RIFF, Magic, MSIZE_RIFF);
    if (ID_CSTR (Magic, FM_RIFF, MSIZE_RIFF) && Nbytes > LHMIN_WAVE) {
      RHEAD_SN (fp, MOFFS_WAVE, Magic, MSIZE_WAVE);
      if (ID_CSTR (Magic, FM_WAVE, MSIZE_WAVE))
	return FT_WAVE;
    }
  }

/* Check for an AIFF or AIFF-C file */
  if (Nbytes >= LHMIN_AIFF) {
    RHEAD_SN (fp, MOFFS_IFF, Magic, MSIZE_IFF);
    if (ID_CSTR (Magic, FM_IFF, MSIZE_IFF)) {
      RHEAD_SN (fp, MOFFS_AIFF, Magic, MSIZE_AIFF);
      if (ID_CSTR (Magic, FM_AIFF, MSIZE_AIFF))
	return FT_AIFF;
      else if (ID_CSTR (Magic, FM_AIFF_C, MSIZE_AIFF_C))
	return FT_AIFF_C;
    }
  }

/* Check for an NIST SPHERE audio file */
  if (Nbytes >= LHMIN_SPHERE) {
    RHEAD_SN (fp, MOFFS_SPHERE, Magic, MSIZE_SPHERE);
    if (ID_CSTR (Magic, FM_SPHERE, MSIZE_SPHERE))
      return FT_SPHERE;
  }

/* Check for an ESPS sampled data feature file */
/* ESPS files can be written in either byte order */
  if (Nbytes >= LHMIN_ESPS) {
    RHEAD_SN (fp, MOFFS_ESPS, Magic, MSIZE_ESPS);
    if (ID_CSTR (Magic, FM_ESPS, MSIZE_ESPS) ||
	ID_CSTR (Magic, FM_ESPS_SWAPPED, MSIZE_ESPS))
      return FT_ESPS;
  }

/* Check for an IRCAM soundfile */
  if (Nbytes >= LHMIN_SF) {
    RHEAD_SN (fp, MOFFS_SF, Magic, MSIZE_SF);
    if (ID_CSTR (Magic, FM_SF_VAX_L, MSIZE_SF) ||
	ID_CSTR (Magic, FM_SF_VAX_B, MSIZE_SF) ||
	ID_CSTR (Magic, FM_SF_SUN, MSIZE_SF) ||
	ID_CSTR (Magic, FM_SF_MIPS_L, MSIZE_SF) ||
	ID_CSTR (Magic, FM_SF_MIPS_B, MSIZE_SF) ||
	ID_CSTR (Magic, FM_SF_NEXT, MSIZE_SF))
      return FT_SF;
  }

/* Check for an SPPACK file */
  if (Nbytes >= LHMIN_SPPACK) {
    RHEAD_SN (fp, MOFFS_SPPACK, Magic, MSIZE_SPPACK);
    if (ID_CSTR (Magic, FM_SPPACK, MSIZE_SPPACK))
      return FT_SPPACK;
  }

/* Check for an INRS-Telecommunications audio file */
  if (Nbytes >= LHMIN_INRS) {
    RHEAD_SN (fp, MOFFS_INRS, Magic, MSIZE_INRS);
    for (i = 0; i < NINRS; i++) {
      if (ID_CSTR (Magic, FM_INRS[i], MSIZE_INRS))
        return FT_INRS;
    }
  }

/* Check for a text audio file */
  if (Nbytes >= LHMIN_TXAUD) {
    RHEAD_SN (fp, MOFFS_TXAUD, Magic, MSIZE_TXAUD);
    if (ID_CSTR (Magic, FM_TXAUD, MSIZE_TXAUD))
      return FT_TXAUD;
  }

/* Check if a headerless (non-standard) file should be accepted */
  if (Fdef->Accept == NH_ACCEPT)
    return FT_NH;

/* No match, declare it an unknown file type */
  return FT_UNKNOWN;
}