

/*
#    Sfront, a SAOL to C translator    
#    This file: Handles asys interfaces
#    Copyright (C) 1999  Regents of the University of California
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License (Version 2) as
#    published by the Free Software Foundation.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#    Maintainer: John Lazzaro, lazzaro@cs.berkeley.edu
*/

#include "tree.h"

#define PORTAUDIO_OSS_TESTING      0  /* 1 to add Portaudio OSS driver */
#define DEPRECIATED_DSOUND_TESTING 0  /* 1 to add broken DSound driver */

/****************************************************************/
/* types of audio devices -- add your driver to the end         */
/*                and update DRIVER_END.                        */
/****************************************************************/

#define DRIVER_NONE         0
#define DRIVER_STD          1
#define DRIVER_NULL         2
#define DRIVER_RAW          3
#define DRIVER_WAV          4
#define DRIVER_AIF          5
#define DRIVER_HPUX         6
#define DRIVER_DSOUND       7
#define DRIVER_LINUX        8
#define DRIVER_VCDAT        9
#define DRIVER_FREEBSD     10
#define DRIVER_IRIX        11
#define DRIVER_PA_UNIX_OSS 12
#define DRIVER_PA_WIN_WMME 13
#define DRIVER_PA_WIN_DS   14
#define DRIVER_COREAUDIO   15
#define DRIVER_END         16

/****************************************************************/
/*             prints help screen for audio options             */
/****************************************************************/

void printaudiohelp(void)

{

  printf("Where C output binary sends its audio:\n");
  printf("       [-aout fname.wav]   WAV file fname.wav\n");
  printf("       [-aout fname.aif]   AIFF file fname.aif\n");
  printf("       [-aout fname.raw]   16-bit integers to fname.raw\n");
  printf("       [-aout fname.dat]   View .dat files\n");
  printf("       [-aout std]         stdout\n");
  printf("       [-aout null]        do nothing with the output\n");
  printf("       [-aout linux]       soundcard under Linux (OSS).\n");
  printf("       [-aout pa_win_wmme] soundcard under Windows (WMME API).\n");
  printf("       [-aout pa_win_ds]   soundcard under Windows (Direct API).\n");
  printf("       [-aout coreaudio]   soundcard under Mac OS X (CoreAudio).\n");
  printf("       [-aout freebsd]     soundcard under FreeBSD.\n");
  printf("       [-aout irix]        soundcard under IRIX (6.2 or later).\n");
  printf("       [-aout hpux]        soundcard under HPUX.\n");
  if (PORTAUDIO_OSS_TESTING)
    printf("       [-aout pa_unix_oss] soundcard under UNIX OSS systems.\n");
  if (DEPRECIATED_DSOUND_TESTING)
    printf("       [-aout dsound]      depreciated Directsound driver.\n");
  printf("Where C output binary gets its audio:\n");
  printf("       [-ain fname.wav]    WAV file fname.wav\n");
  printf("       [-ain fname.aif]    AIFF file fname.aif\n");
  printf("       [-ain fname.raw]    16-bit integers from fname.raw\n");
  printf("       [-ain fname.dat]    View .dat files\n");
  printf("       [-ain std]          stdin\n");
  printf("       [-ain null]         silent input audio\n");
  printf("       [-ain linux]        soundcard under Linux (OSS).\n");
  printf("       [-ain pa_win_wmme]  soundcard under Windows (WMME API).\n");
  printf("       [-ain pa_win_ds]    soundcard under Windows (Direct API).\n");
  printf("       [-ain coreaudio]    soundcard under Mac OS X (CoreAudio).\n");
  printf("       [-ain freebsd]      soundcard under FreeBSD.\n");
  printf("       [-ain irix]         soundcard under IRIX (6.2 or later).\n");
  printf("       [-ain hpux]         soundcard under HPUX.\n");
  if (PORTAUDIO_OSS_TESTING)
    printf("       [-ain pa_unix_oss]  soundcard under UNIX OSS systems.\n");
}

/****************************************************************/
/*             maps -aout name to symbol                        */
/****************************************************************/

int aoutfilecheck(char * fname)

{
  int i, found;

  aoutname = dupval(fname);

  /* first, non-filename entries */

  if (!strcmp(fname,"std"))
    {
      aout = DRIVER_STD;
      aoutflow = PASSIVE_FLOW;
      return 0;
    }
  if (!strcmp(fname,"null"))
    {
      aout = DRIVER_NULL;
      aoutflow = PASSIVE_FLOW;
      return 0;
    }
  if (!strcmp(fname,"linux"))
    {
      aout = DRIVER_LINUX;
      aoutflow = PASSIVE_FLOW;
      return 0;
    }
  if (!strcmp(fname,"freebsd"))
    {
      aout = DRIVER_FREEBSD;
      aoutflow = PASSIVE_FLOW;
      return 0;
    }
  if (!strcmp(fname,"hpux"))
    {
      aout = DRIVER_HPUX;
      aoutflow = PASSIVE_FLOW;
      return 0;
    }
  if (!strcmp(fname,"irix"))
    {
      aout = DRIVER_IRIX;
      aoutflow = PASSIVE_FLOW;
      printf("IRIX driver users note:\n");
      printf("  Link using -laudio option.\n\n");
      return 0;
    }
  if (PORTAUDIO_OSS_TESTING && (!strcmp(fname,"pa_unix_oss")))
    {
      aout = DRIVER_PA_UNIX_OSS;
      aoutflow = ACTIVE_FLOW;

      if (ain != DRIVER_PA_UNIX_OSS)
	{
	  printf("Generic UNIX OSS driver note:\n");
	  printf("[1] Use the OS-specific drivers (linux, irix, etc) if\n");
	  printf("    possible; use pa_unix_oss for unsupported systems.\n");
	  printf("[2] Compile with: gcc -O3 sa.c -lm -lpthread -o sa\n\n");
	}
      return 0;
    }
  if (!strcmp(fname,"pa_win_wmme"))
    {
      aout = DRIVER_PA_WIN_WMME;
      aoutflow = ACTIVE_FLOW;

      if (ain != DRIVER_PA_WIN_WMME)
	{
	  printf("Windows MultiMedia Extension API note:\n");
	  printf("[1] MS VC++ compile: cl sa.c winmm.lib -o sa.exe\n");
	  printf("[2] gcc compile:  gcc -O3 sa.c -lm -lwinmm -o sa.exe\n");
	  printf("[3] Also try pa_win_ds, a DirectSound driver.\n\n");
	}
      return 0;
    }
  if (!strcmp(fname,"pa_win_ds"))
    {
      aout = DRIVER_PA_WIN_DS;
      aoutflow = ACTIVE_FLOW;

      if (ain != DRIVER_PA_WIN_DS)
	{
	  printf("Windows DirectSound notes:\n");
	  printf("[1] Developer Microsoft DirectSound must be installed:\n");
	  printf("    http://www.microsoft.com/directx/download.asp\n");
	  printf("[2] MS VC++ compile:\n");
	  printf("    "
		 "cl sa.c dsound.lib dxguid.lib winmm.lib -o sa.exe\n");
	  printf("[3] The sa.c file might not compile under gcc.\n");
	  printf("[4] Also try pa_win_wmme, WMME API driver.\n\n"); 
	}
      return 0;
    }
  if (!strcmp(fname,"coreaudio"))
    {
      aout = DRIVER_COREAUDIO;
      aoutflow = ACTIVE_FLOW;

      found = 0;

      for (i = 2; i < sfront_argc; i++)
	if (!strcmp("-cin", sfront_argv[i-1]))
	  {
	    found = !strcmp("coremidi", sfront_argv[i]);
	    break;
	  }

      if (ain != DRIVER_COREAUDIO)
	{
	  printf("Macintosh OS X CoreAudio notes:\n");

	  if (found)
	    {
	      printf("[1] cc -O3 sa.c -lm -framework coreaudio" 
		     " -framework coremidi \\\n");
	      printf("    -framework corefoundation -o sa\n");
	    }
	  else
	    printf("[1] cc -O3 sa.c -lm -framework CoreAudio -o sa\n");

	  printf("\n");
	}

      return 0;
    }
  if (DEPRECIATED_DSOUND_TESTING && (!strcmp(fname,"dsound")))
    {
      aout = DRIVER_DSOUND;
      aoutflow = PASSIVE_FLOW;

      printf("Dsound driver users note:\n");
      printf("[1] Try the newer pa_win_wmme or pa_win_ds drivers.\n");
      printf("[2] Dsound driver does not compile under gcc.\n");
      printf("[3] Link dxguid.lib and dsound.lib when compiling sa.c.\n");
      printf("[4] Interactive mode (i.e. -cin win32) not working well.\n\n");

      return 0;
    }

  /* then, filename entries */

  if (strstr(fname,".raw") != NULL)
    {
      aout = DRIVER_RAW;
      aoutflow = PASSIVE_FLOW;
      return 0;
    }
  if (strstr(fname,".wav") != NULL)
    {
      aout = DRIVER_WAV;
      aoutflow = PASSIVE_FLOW;
      return 0;
    }
  if (strstr(fname,".aif") != NULL)
    {
      aout = DRIVER_AIF;
      aoutflow = PASSIVE_FLOW;
      return 0;
    }
  if (strstr(fname,".dat") != NULL)
    {
      aout = DRIVER_VCDAT;
      aoutflow = PASSIVE_FLOW;
      return 0;
    }

  return 1;
}

/****************************************************************/
/*             maps -ain commands to symbol                     */
/****************************************************************/

int ainfilecheck(char * fname)

{
  ainname = dupval(fname);

  /* first, non-filename entries */

  if (!strcmp(fname,"std"))
    {
      ain = DRIVER_STD;
      ainflow = PASSIVE_FLOW;
      ainlatency = LOW_LATENCY_DRIVER; 
      return 0;
    }
  if (!strcmp(fname,"null"))
    {
      ain = DRIVER_NULL;
      ainflow = PASSIVE_FLOW;
      ainlatency = HIGH_LATENCY_DRIVER; 
      return 0;
    }
  if (!strcmp(fname,"linux"))
    {
      ain = DRIVER_LINUX;
      ainflow = PASSIVE_FLOW;
      ainlatency = LOW_LATENCY_DRIVER; 
      return 0;
    }
  if (!strcmp(fname,"freebsd"))
    {
      ain = DRIVER_FREEBSD;
      ainflow = PASSIVE_FLOW;
      ainlatency = LOW_LATENCY_DRIVER; 
      return 0;
    }
  if (!strcmp(fname,"irix"))
    {
      ain = DRIVER_IRIX;
      ainflow = PASSIVE_FLOW;
      ainlatency = LOW_LATENCY_DRIVER; 
      return 0;
    }
  if (!strcmp(fname,"hpux"))
    {
      ain = DRIVER_HPUX;
      ainflow = PASSIVE_FLOW;
      ainlatency = LOW_LATENCY_DRIVER; 
      return 0;
    }
  if (PORTAUDIO_OSS_TESTING && (!strcmp(fname,"pa_unix_oss")))
    {
      ain = DRIVER_PA_UNIX_OSS;
      ainflow = ACTIVE_FLOW;
      ainlatency = LOW_LATENCY_DRIVER; 

      if (aout != DRIVER_PA_UNIX_OSS)
	{
	  printf("Generic UNIX OSS driver note:\n");
	  printf("[1] Use the OS-specific drivers (linux, irix, etc) if\n");
	  printf("    possible; use pa_unix_oss for unsupported systems.\n");
	  printf("[2] Compile with: gcc -O3 sa.c -lm -lpthread -o sa\n\n");
	}
      return 0;
    }
  if (!strcmp(fname,"pa_win_wmme"))
    {
      ain = DRIVER_PA_WIN_WMME;
      ainflow = ACTIVE_FLOW;
      ainlatency = LOW_LATENCY_DRIVER; 

      if (aout != DRIVER_PA_WIN_WMME)
	{
	  printf("Windows MultiMedia Extension API note:\n");
	  printf("[1] MS VC++ compile: cl sa.c winmm.lib -o sa.exe\n");
	  printf("[2] gcc compile:  gcc -O3 sa.c -lm -lwinmm -o sa.exe\n");
	  printf("[3] Also try pa_win_ds, the DirectSound driver.\n\n");
	}
      return 0;
    }
  if (!strcmp(fname,"pa_win_ds"))
    {
      ain = DRIVER_PA_WIN_DS;
      ainflow = ACTIVE_FLOW;
      ainlatency = LOW_LATENCY_DRIVER; 

      if (aout != DRIVER_PA_WIN_DS)
	{
	  printf("Windows DirectSound notes:\n");
	  printf("[1] Developer Microsoft DirectSound must be installed:\n");
	  printf("    http://www.microsoft.com/directx/download.asp\n");
	  printf("[2] MS VC++ compile:\n");
	  printf("    "
		 "cl sa.c dsound.lib dxguid.lib winmm.lib -o sa.exe\n");
	  printf("[3] The sa.c file might not compile under gcc.\n");
	  printf("[4] Also try pa_win_wmme, the WMME API driver.\n\n"); 
	}
      return 0;
    }
  if (!strcmp(fname,"coreaudio"))
    {
      ain = DRIVER_COREAUDIO;
      ainflow = ACTIVE_FLOW;
      ainlatency = LOW_LATENCY_DRIVER; 

      if (aout != DRIVER_COREAUDIO)
	{
	  printf("Macintosh OS X CoreAudio notes:\n");
          printf("[1] cc -O3 sa.c -lm -framework CoreAudio -o sa\n\n");
	}
      return 0;
    }

  /* then, filename entries */

  if (strstr(fname,".raw") != NULL)
    {
      ain = DRIVER_RAW;
      ainflow = PASSIVE_FLOW;
      ainlatency = HIGH_LATENCY_DRIVER; 
      return 0;
    }
  if (strstr(fname,".wav") != NULL)
    {
      ain = DRIVER_WAV;
      ainflow = PASSIVE_FLOW;
      ainlatency = HIGH_LATENCY_DRIVER; 
      return 0;
    }
  if (strstr(fname,".aif") != NULL)
    {
      ain = DRIVER_AIF;
      ainflow = PASSIVE_FLOW;
      ainlatency = HIGH_LATENCY_DRIVER; 
      return 0;
    }
  if (strstr(fname,".dat") != NULL)
    {
      ain = DRIVER_VCDAT;
      ainflow = PASSIVE_FLOW;
      ainlatency = HIGH_LATENCY_DRIVER; 
      return 0;
    }

  return 1;
}

/****************************************************************/
/*             returns sample type for output                  */
/****************************************************************/

int makeaudiotypeout(int anum)

{
  int ret = SAMPLE_SHORT;

  switch (anum) {
  case DRIVER_PA_UNIX_OSS:
  case DRIVER_PA_WIN_WMME:
  case DRIVER_PA_WIN_DS:
  case DRIVER_COREAUDIO:
  case DRIVER_VCDAT:
    ret = SAMPLE_FLOAT;
    break;
  default:
    break;
  }
  return ret;
}

/****************************************************************/
/*             returns sample type for input                    */
/****************************************************************/

int makeaudiotypein(int anum)

{
  int ret = SAMPLE_SHORT;

  switch (anum) {
  case DRIVER_PA_UNIX_OSS:
  case DRIVER_PA_WIN_WMME:
  case DRIVER_PA_WIN_DS:
  case DRIVER_COREAUDIO:
  case DRIVER_VCDAT:
    ret = SAMPLE_FLOAT;
    break;
  default:
    break;
  }
  return ret;
}

/****************************************************************/
/*  audio output driver request function for ksync duties       */
/****************************************************************/

int makeaoutsync(int anum)

{
  int ret;

  switch (anum) {
  case DRIVER_LINUX:     /* included in audio driver file */  
  case DRIVER_FREEBSD:    
  case DRIVER_DSOUND:
  case DRIVER_PA_UNIX_OSS:
  case DRIVER_PA_WIN_WMME:
  case DRIVER_PA_WIN_DS:
  case DRIVER_COREAUDIO:
    ret = 1;
    break;
  default:               /* use standard UNIX version */
    ret = 0;
    break;
  }
  return ret;
}

/****************************************************************/
/*   audio input driver request function for ksync duties       */
/****************************************************************/

int makeainsync(int anum)

{
  int ret;

  switch (anum) {
  case DRIVER_PA_UNIX_OSS:    /* only here as an example, may not work */
  case DRIVER_PA_WIN_WMME:
  case DRIVER_PA_WIN_DS:
  case DRIVER_COREAUDIO:
    ret = 1;
    break;
  default:               
    ret = 0;
    break;
  }
  return ret;
}

/****************************************************************/
/*        preferred time option for audio output driver         */
/****************************************************************/

int makeaouttimedefault(int anum)

{
  int ret = RENDER;  /* assign PLAYBACK or TIMESYNC below */

  switch (anum) {
  case DRIVER_LINUX:
  case DRIVER_FREEBSD:
  case DRIVER_IRIX:
  case DRIVER_HPUX:
  case DRIVER_DSOUND:
  case DRIVER_PA_UNIX_OSS:
  case DRIVER_PA_WIN_WMME:
  case DRIVER_PA_WIN_DS:
  case DRIVER_COREAUDIO:
    ret = PLAYBACK;
    break;
  }

  return ret;
}


/****************************************************************/
/*        preferred time option for audio input driver          */
/****************************************************************/

int makeaintimedefault(int anum)

{
  int ret = RENDER;  /* assign PLAYBACK or TIMESYNC below */

  switch (anum) {
  case DRIVER_LINUX:
  case DRIVER_FREEBSD:
  case DRIVER_IRIX:
  case DRIVER_HPUX:
  case DRIVER_DSOUND:
  case DRIVER_PA_UNIX_OSS:
  case DRIVER_PA_WIN_WMME:
  case DRIVER_PA_WIN_DS:
  case DRIVER_COREAUDIO:
    ret = PLAYBACK;
    break;
  }

  return ret;
}


/****************************************************************/
/*   returns number of channels and sample rate for input       */
/*   important points:                                          */
/*        [1] only called when SAOL program doesn't specify     */
/*            global parameters srate and/or inchannels.        */
/*        [2] If sample rate is unknown, the *srate value       */
/*            will be -1, if known it will be 0. If the         */
/*            If the number of input channels is unknown,       */ 
/*            *inchannels will be -1, if known it will be 0.    */
/*        [3] if you know an unknown value (for example, by     */
/*            opening a file and reading its preamble)          */
/*            assign into its variable. assignment to known     */
/*            quantities is ignored.                            */
/*        [4] if you don't assign unknown quantities, then      */
/*            *srate = 32000 and *inchannels = 0 are defaults.  */
/*        [5] if you're doing heavy lifting to figure these     */
/*            values out, add to the extern list that precedes  */
/*            the function, and add a function AT THE END OF    */ 
/*            THE FILE. use the naming convention               */
/*            ainparams_drivername().                           */
/*        [6] this routine is called after global variable      */
/*            char * ainname is set by ainfilecheck             */
/****************************************************************/

extern void ainparams_wav(int * srate, int * inchannels);
extern void ainparams_aif(int * srate, int * inchannels);
extern void ainparams_info(int * srate, int * inchannels);

void makeainparams(int anum, int * srate, int * inchannels)

{

  switch (anum) {
  case DRIVER_WAV:
    ainparams_wav(srate, inchannels);
    break;
  case DRIVER_AIF:
    ainparams_aif(srate, inchannels);
    break;
  case DRIVER_RAW:
  case DRIVER_STD:
    ainparams_info(srate, inchannels);
    break;
  case DRIVER_VCDAT:
    *inchannels = 1;
    if (*srate < 0)
      warningmessage(0,"Assuming sample rate of -ain file is 44100Hz");
    *srate = 44100;
    break;
  case DRIVER_NULL:   
  case DRIVER_HPUX:   
  case DRIVER_IRIX:   
  case DRIVER_DSOUND: 
  case DRIVER_LINUX:  
  case DRIVER_FREEBSD:
  case DRIVER_PA_UNIX_OSS:
  case DRIVER_PA_WIN_WMME:
  case DRIVER_PA_WIN_DS:
  case DRIVER_COREAUDIO:
    /* modern soundcards optimized to this configuration */
    *inchannels = 2;
    *srate = 44100;
    break;
  default:
    break;
  }

}

/****************************************************************/
/*         returns 1 if aout driver sends audio on stdout       */
/****************************************************************/

int stdoutused(int anum)

{
  return (anum == DRIVER_STD);
}

/****************************************************************/
/*         returns 1 if aout driver gets audio from stdin       */
/****************************************************************/

int stdinused(int anum)

{
  return (anum == DRIVER_STD);
}

/****************************************************************/
/*                 sets defaults for audio system               */
/****************************************************************/

void audiodefaults(void)

{
  if (!ain)
    {
      ain = DRIVER_WAV;
      ainname = dupval("input.wav");
      ainlatency = HIGH_LATENCY_DRIVER; 
      ainflow = PASSIVE_FLOW;
    }
  if (!aout)
    {
      aout = DRIVER_WAV;
      aoutname = dupval("output.wav");
      aoutflow = PASSIVE_FLOW;
    }
  if (timeoptions == UNKNOWN)
    {
      timeoptions = makeaouttimedefault(aout);
      if (makeaintimedefault(ain) > timeoptions)
	timeoptions = makeaintimedefault(ain);
    }
}

/****************************************************************/
/*         portaudio driver files common to all platforms       */
/****************************************************************/

void makeportaudiocommon(void)

{
  makepa_porthdr();
  makepa_hosthdr();
  makepa_tracehdr();
  makepa_lib();
  makeportaudio();
}

/****************************************************************/
/*    calls function that includes driver C code in sa.c file   */
/****************************************************************/

void makeaudiodriver(int anum)

{
  switch (anum) {
  case DRIVER_STD:
    makestd();
    break;
  case DRIVER_NULL:
    makenull();
    break;
  case DRIVER_RAW:
    makeraw();
    break;
  case DRIVER_LINUX:
    makelinux();
    break;
  case DRIVER_FREEBSD:
    makefreebsd();
    break;
  case DRIVER_HPUX:
    makehpux();
    break;
  case DRIVER_IRIX:
    makeirix();
    break;
  case DRIVER_DSOUND:
    makedsound();
    break;
  case DRIVER_WAV:
    makewav();
    break;
  case DRIVER_AIF:
    makeaif();
    break;
  case DRIVER_VCDAT:
    makevcdat();
    break;
  case DRIVER_PA_UNIX_OSS:
    makeportaudiocommon();
    makepa_unix_oss();
    break;
  case DRIVER_PA_WIN_WMME:
    makeportaudiocommon();
    makepa_win_wmme();
    break;
  case DRIVER_COREAUDIO:
    makecoreaudio();
    break;
  case DRIVER_PA_WIN_DS:
    makeportaudiocommon();
    makepa_dshdr();
    makepa_dswrap();
    makepa_dsound();
    break;
  default:
    internalerror("audio.c","makeaudiodriver()");
  }
}

/****************************************************************/
/*           The Beginning of the End of the File               */
/*       The start of list of ainparams_mydriver functions      */
/*            New functions pre-pended to the end.              */
/****************************************************************/

/****************************************************************/
/*           Code block for the ainparams_info() function        */
/****************************************************************/


/*********************************************************/
/*          the function called by makeainparams()       */
/*********************************************************/

void ainparams_info(int * srate, int * inchannels)
     
{
  FILE * fd = NULL;     /* input file pointer */
  char * iname;
  int newint,i;

  if (ainname && (ain == DRIVER_RAW))
    {    
      vmcheck(iname = calloc(6+strlen(ainname), sizeof(char)));
      strcat(strcat(iname,ainname),".info");
      fd = fopen(iname, "r");
      free(iname);
    }
  else
    {
      if (ain == DRIVER_RAW)
	fd = fopen("input.raw.info","rb");
      else
	if (ain == DRIVER_STD)
	  fd = fopen("stdin.info","rb");
    }
  if (!fd)
    return;


  if (1 != (i = fscanf(fd, "%i", &newint)))
    {
      fclose(fd);
      return;
    }

  if (newint > 0)
    *srate = newint;

  if (1 != fscanf(fd, "%i", &newint))
    {
      fclose(fd);
      return;
    }

  if (newint > 0)
    *inchannels = newint;

  fclose(fd);
  return;

}


/****************************************************************/
/*           Code block for the ainparams_wav() function        */
/****************************************************************/


#define AINPARAMS_WAV_MATCH   0
#define AINPARAMS_WAV_EOF     1
#define AINPARAMS_WAV_NOMATCH 2

#define AINPARAMS_WAV_DONE    0
#define AINPARAMS_WAV_ERROR   1

/*********************************************************/
/*        flushes next block of WAV bytes                */
/*********************************************************/

int ainparams_wav_flushbytes(FILE * fd, int numbytes)

{
  unsigned char c;

  while (numbytes > 0)
    {
      if (fread(&c, sizeof(char), 1, fd) != 1)
	return AINPARAMS_WAV_ERROR;
      numbytes--;
    }
  return AINPARAMS_WAV_DONE;

}

  
/***********************************************************/
/*  checks byte stream for AIFF/WAV cookie --              */
/***********************************************************/

int ainparams_wav_soundtypecheck(FILE * fd, char * d)

{
  char c[4];

  if (fread(c, sizeof(char), 4, fd) != 4)
    return AINPARAMS_WAV_EOF;
  if (strncmp(c,d,4))
    return AINPARAMS_WAV_NOMATCH;
  return AINPARAMS_WAV_MATCH;
}


/*********************************************************/
/*            gets next block of WAV bytes               */
/*********************************************************/

int ainparams_wav_getbytes(FILE * fd, unsigned char * c, int numbytes)

{
  if ((int)fread(c, sizeof(char), numbytes, fd) != numbytes)
    return AINPARAMS_WAV_ERROR;
  return AINPARAMS_WAV_DONE;
}


/*********************************************************/
/*     converts byte stream to an unsigned long          */
/*********************************************************/

int ainparams_wav_getlong(FILE * fd, int numbytes, unsigned long * ret)

{
  unsigned char c[4];

  if (numbytes > 4)
    return AINPARAMS_WAV_ERROR;
  if (AINPARAMS_WAV_DONE != ainparams_wav_getbytes(fd, &c[0],numbytes))
    return AINPARAMS_WAV_ERROR;
  switch (numbytes) {
  case 4:
    *ret  =  (unsigned long)c[0];
    *ret |=  (unsigned long)c[1] << 8;
    *ret |=  (unsigned long)c[2] << 16;
    *ret |=  (unsigned long)c[3] << 24;
    return AINPARAMS_WAV_DONE;
  case 3:
    *ret  =  (unsigned long)c[0];
    *ret |=  (unsigned long)c[1] << 8;
    *ret |=  (unsigned long)c[2] << 16;
    return AINPARAMS_WAV_DONE;
  case 2:
    *ret  =  (unsigned long)c[0];
    *ret |=  (unsigned long)c[1] << 8;
    return AINPARAMS_WAV_DONE;
  case 1:
    *ret = (unsigned long)c[0];
    return AINPARAMS_WAV_DONE;
  default:
    return AINPARAMS_WAV_ERROR;
  }

}

/*********************************************************/
/*          reads the header of a wave file              */
/*********************************************************/

FILE * ainparams_wav_reader(FILE * fd, int * srate, int * inchannels)

{
  unsigned long i, cookie;
  long len;

  if ((ainparams_wav_soundtypecheck(fd, "RIFF")!= AINPARAMS_WAV_MATCH) ||
      (ainparams_wav_flushbytes(fd, 4)!= AINPARAMS_WAV_DONE) ||
      (ainparams_wav_soundtypecheck(fd, "WAVE")!= AINPARAMS_WAV_MATCH))
    return fd;
  
  while ((cookie = ainparams_wav_soundtypecheck(fd, "fmt "))
	 != AINPARAMS_WAV_MATCH)
    {
      if ((cookie == AINPARAMS_WAV_EOF) || 
	  (ainparams_wav_getlong(fd, 4, &i) != AINPARAMS_WAV_DONE))
	return fd;
      if (ainparams_wav_flushbytes(fd, i)!= AINPARAMS_WAV_DONE)
	return fd;
    }

  if (ainparams_wav_getlong(fd, 4, &i) != AINPARAMS_WAV_DONE)
    return fd;

  len = i;
  if (((len -= 16) < 0) ||
      (ainparams_wav_getlong(fd, 2, &i) != AINPARAMS_WAV_DONE))
    return fd;

  if (i != 1)   /* PCM magic number */
    return fd;

  if (ainparams_wav_getlong(fd, 2, &i) != AINPARAMS_WAV_DONE)
    return fd;

  *inchannels = i;

  if (ainparams_wav_getlong(fd, 4, &i) != AINPARAMS_WAV_DONE)
    return fd;
  
  *srate = i;
  return fd;
}


/*********************************************************/
/*          the function called by makeainparams()       */
/*********************************************************/

void ainparams_wav(int * srate, int * inchannels)
     
{
  FILE * fd;     /* input file pointer */
  
  if (ainname)
    fd = fopen(ainname,"rb");
  else
    fd = fopen("output.wav","rb");
  
  if (!fd)
    return;

  fclose(ainparams_wav_reader(fd, srate, inchannels));

}

#undef AINPARAMS_WAV_MATCH  
#undef AINPARAMS_WAV_EOF 
#undef AINPARAMS_WAV_NOMATCH 
#undef AINPARAMS_WAV_DONE    
#undef AINPARAMS_WAV_ERROR   


/****************************************************************/
/*           Code block for the ainparams_aif() function        */
/****************************************************************/


#define AINPARAMS_AIF_MATCH   0
#define AINPARAMS_AIF_EOF     1
#define AINPARAMS_AIF_NOMATCH 2

#define AINPARAMS_AIF_DONE    0
#define AINPARAMS_AIF_ERROR   1
    

/***********************************************************/
/*  checks byte stream for AIFF cookie --                 */
/***********************************************************/

int ainparams_aif_soundtypecheck(FILE * fd, char * d)

{
  char c[4];

  if (fread(c, sizeof(char), 4, fd) != 4)
    return AINPARAMS_AIF_EOF;
  if (strncmp(c,d,4))
    return AINPARAMS_AIF_NOMATCH;
  return AINPARAMS_AIF_MATCH;
}
  
/*********************************************************/
/*        flushes next block of AIFF bytes                */
/*********************************************************/

int ainparams_aif_flushbytes(FILE * fd, int numbytes)

{
  unsigned char c;

  while (numbytes > 0)
    {
      if (fread(&c, sizeof(char), 1, fd) != 1)
	return AINPARAMS_AIF_ERROR;
      numbytes--;
    }
  return AINPARAMS_AIF_DONE;

}

/*********************************************************/
/*            gets next block of AIFF bytes               */
/*********************************************************/

int ainparams_aif_getbytes(FILE * fd, unsigned char * c, int numbytes)

{
  if ((int)fread(c, sizeof(char), numbytes, fd) != numbytes)
    return AINPARAMS_AIF_ERROR;
  return AINPARAMS_AIF_DONE;
}

/*********************************************************/
/*     converts byte stream to an unsigned long          */
/*********************************************************/

int ainparams_aif_getlong(FILE * fd, int numbytes, unsigned long * ret)

{
  unsigned char c[4];

  if (numbytes > 4)
    return AINPARAMS_AIF_ERROR;
  if (AINPARAMS_AIF_DONE != ainparams_aif_getbytes(fd, &c[0],numbytes))
    return AINPARAMS_AIF_ERROR;
  switch (numbytes) {
  case 4:
    *ret  =  (unsigned long)c[3];
    *ret |=  (unsigned long)c[2] << 8;
    *ret |=  (unsigned long)c[1] << 16;
    *ret |=  (unsigned long)c[0] << 24;
    return AINPARAMS_AIF_DONE;
  case 3:
    *ret  =  (unsigned long)c[2];
    *ret |=  (unsigned long)c[1] << 8;
    *ret |=  (unsigned long)c[0] << 16;
    return AINPARAMS_AIF_DONE;
  case 2:
    *ret  =  (unsigned long)c[1];
    *ret |=  (unsigned long)c[0] << 8;
    return AINPARAMS_AIF_DONE;
  case 1:
    *ret = (unsigned long)c[0];
    return AINPARAMS_AIF_DONE;
  default:
    return AINPARAMS_AIF_ERROR;
  }

}
    
/*********************************************************/
/*     converts byte stream to an long                   */
/*********************************************************/

int ainparams_aif_getslong(FILE * fd, int numbytes, long * ret)

{
  unsigned char c[4];

  if (numbytes > 4)
    return AINPARAMS_AIF_ERROR;
  if (AINPARAMS_AIF_DONE != ainparams_aif_getbytes(fd, &c[0],numbytes))
    return AINPARAMS_AIF_ERROR;
  switch (numbytes) {
  case 4:
    *ret  =  (long)c[3];
    *ret |=  (long)c[2] << 8;
    *ret |=  (long)c[1] << 16;
    *ret |=  (long)c[0] << 24;
    return AINPARAMS_AIF_DONE;
  case 3:
    *ret  =  (long)c[2];
    *ret |=  (long)c[1] << 8;
    *ret |=  (long)c[0] << 16;
    return AINPARAMS_AIF_DONE;
  case 2:
    *ret  =  (long)c[1];
    *ret |=  (long)c[0] << 8;
    return AINPARAMS_AIF_DONE;
  case 1:
    *ret = (long)c[0];
    return AINPARAMS_AIF_DONE;
  default:
    return AINPARAMS_AIF_ERROR;
  }

}
    
/*********************************************************/
/*          reads the header of a wave file              */
/*********************************************************/

FILE * ainparams_aif_reader(FILE * fd, int * srate, int * inchannels)

{
  unsigned long i, m;
  long e;
  unsigned char c[4];


  if (ainparams_aif_soundtypecheck(fd,"FORM")!= AINPARAMS_AIF_MATCH)
    return fd;
  if (ainparams_aif_flushbytes(fd, 4)!= AINPARAMS_AIF_DONE)
    return fd;
  if (ainparams_aif_soundtypecheck(fd, "AIFF")!= AINPARAMS_AIF_MATCH)
    return fd;
  if (ainparams_aif_getbytes(fd, &c[0],4)!= AINPARAMS_AIF_DONE)
    return fd;

  while (strncmp((char *) c,"COMM",4))
    {
      if (ainparams_aif_getlong(fd,4, &i) != AINPARAMS_AIF_DONE)
	return fd;
      if (ainparams_aif_flushbytes(fd, i)!= AINPARAMS_AIF_DONE)
	return fd;
      if (ainparams_aif_getbytes(fd, &c[0], 4)!= AINPARAMS_AIF_DONE)
	return fd;
    }


  if (ainparams_aif_flushbytes(fd, 4)!= AINPARAMS_AIF_DONE) /* length */
    return fd;
  if (ainparams_aif_getlong(fd, 2, &i) != AINPARAMS_AIF_DONE)
    return fd;
  if (i > 0)
    *inchannels = i;

  if (ainparams_aif_flushbytes(fd, 4)!= AINPARAMS_AIF_DONE) /* frames */
    return fd;
  if (ainparams_aif_getlong(fd, 2, &i) != AINPARAMS_AIF_DONE)
    return fd;
  if (i != 16)
    {
      fprintf(stderr,"Error: Can't handle %li bit data\n",i);
      return fd;
    }
  if (ainparams_aif_getslong(fd, 2, &e) != AINPARAMS_AIF_DONE)
    return fd;
  if (ainparams_aif_getlong(fd, 4, &m) != AINPARAMS_AIF_DONE)
    return fd;

  i = (unsigned long)(0.5+(m*exp(log(2)*(e - 16414.0F))));
  if (i > 0)
    *srate = i;

  return fd;


}

/*********************************************************/
/*          the function called by makeainparams()       */
/*********************************************************/

void ainparams_aif(int * srate, int * inchannels)
     
{
  FILE * fd;     /* input file pointer */
  
  if (ainname)
    fd = fopen(ainname,"rb");
  else
    fd = fopen("output.aif","rb");
  
  if (!fd)
    return;

  fclose(ainparams_aif_reader(fd, srate, inchannels));

}

#undef AINPARAMS_AIF_MATCH  
#undef AINPARAMS_AIF_EOF 
#undef AINPARAMS_AIF_NOMATCH 
#undef AINPARAMS_AIF_DONE    
#undef AINPARAMS_AIF_ERROR   



/****************************************************************/
/*         end of list of ainparams_mydriver functions          */
/****************************************************************/

