/*****************************************************************
play & record audio

$Id: plax.c,v 1.52 2003/12/09 04:06:17 purnhage Exp $

Source file: plax.c

(c) Heiko Purnhagen 1998-2003
e-mail: purnhage@tnt.uni-hannover.de
WWW:    http://www.tnt.uni-hannover.de/~purnhage/

This program is available under the terms of the GNU General Public
License. See file COPYING for further details. All usual disclaimers
apply!

Authors:
HP    Heiko Purnhagen, Uni Hannover <purnhage@tnt.uni-hannover.de>

Changes:
16-sep-98   HP      first version
18-sep-98   HP      mono/stereo and sample rate conversion
21-sep-98   HP      fixed numSamp%2=0 for stereo rate conversion
30-dec-98   HP      CVS, evaluates USE_AFSP
07-jan-99   HP      AFsp-v4r1
08-jan-99   HP      audio device selection
11-jan-99   HP      startTime
17-jan-99   HP      ...
22-jan-99   HP      "audio device -> audio device" error message
26-jan-99   HP      improved output file format evaluation
28-jan-99   HP      fixed bufLen
18-feb-99   HP      progress in seconds / "plax" -> reset audio device
21-feb-99   HP      -d 1 gives "plax: * -> *" message
23-feb-99   HP      fixed debug output ...
23-apr-99   HP      fixed gain bug for AFsp output
17-may-99   HP      improved output file format detection
03-aug-99   HP      -d 1 gives "xx.xxx s  x ch   xxx.x Hz   xxx.wav"
04-aug-99   HP      -n for noAudio, leading/trailing silence & fade-in/out
05-aug-99   HP      fixed bug for "-x -td -ts"
04-nov-99   HP/BE   added -df
11-nov-99   HP      added fabs() #include <math.h>
19-nov-99   HP      improved -s -c handling, added -bp
31-jan-00   HP      added -ad, -al, -au
12-feb-00   HP      added -g0, use seek for -ts & AFsp, -df also for rec
15-feb-00   HP      fixed outBufUsed for state==5
                    -ia, -oa for stdin/stdout using AFsp (see also -of xxx)
                    -d uses %i (permits e.g. -d 0xff)
12-mar-00   HP      added -hp -ho (high-pass filter)
07-jun-00   HP      help on -bp 0 to prevent hangup
04-sep-00   HP      test for "file not found" to avoid abort in AFopenRead()
10-sep-00   HP      added -if, fixed "plax -ia -" bug
18-Aug-2001 HP      added fracOff
20-Aug-2001 HP      added 16 bit raw i/o (big/little endian, skip header)
23-Sep-2001 HP      fixed format selection for AuOpenWriteNB()
13-Nov-2001 HP      removed fixed arrays
18-Dec-2001 HP      removed some compiler warnings
04-Jan-2002 HP      adapted to new PRReadData()
01-Feb-2002 HP      adapted to PROpenReadFrag(), PROpenWriteFrag()
20-Mar-2002 HP      fixed PRReadData() error handling
03-Jun-2002 HP      some devFragsize/devLatenc fixes
2003-03-14  HP      resampling & mono-up/down mixing for >2ch
2003-10-11  HP      added -wd and -wl for wplax via stdout
2003-12-09  HP      fixed 1st-sample bug in n->1 downmixing
**********************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <signal.h>
#include <ctype.h>

#ifdef USE_AFSP
#include <libtsp.h>		/* AFsp audio file library */
#include <libtsp/AFpar.h>	/* AFsp audio file library - definitions */
#endif

#include "common.h"		/* common module */
#include "cmdline.h"		/* command line module */
#include "austream.h"		/* audio i/o streams */
#include "playrec.h"		/* audio play & record */
#include "rateconv.h"		/* audio sample rate conversion */


/* ---------- declarations ---------- */

#define PROGVER "plax V1.2.4 09-Dec-2003"
#define CVSID "$Id: plax.c,v 1.52 2003/12/09 04:06:17 purnhage Exp $"
#ifndef DISTVER		/* set in Makefile */
#define DISTVER "<unknown>"
#endif

#ifndef MSVC
#define min(a,b) ((a) < (b) ? (a) : (b))
#define max(a,b) ((a) > (b) ? (a) : (b))
#endif

#define STRLEN (255)

#ifndef PI
#define PI 3.14159265358979323846264338327950288
#endif

#define RAWENV "RAWAUDIOFILE"
#define RAWDEFAULT "integer16,0,48000.,native,1,1.0"

#ifdef USE_AFSP
#ifdef FW_SUN
/* only AFsp-V3R2 available: map AFsp-V3R2 to AFsp-v4r1 */
#define AFsetNHpar AFsetNH
#define FTW_AU (FW_SUN/256)
#define FTW_WAVE (FW_WAVE/256)
#define FTW_AIFF_C (FW_AIFF_C/256)
#define FTW_NH_EB (FW_NH_EB/256)
#endif
#endif


/* ---------- variables ---------- */

/* command line module */

static char *progName = "<no program name>";
static char *outFileName;
static int outFileNameUsed;
static char *outFileFormat;
static int outFileFormatUsed;
static char *inFileFormat;
static int inFileFormatUsed;
static int skipHeader;
static int stdinAFsp;
static int stdoutAFsp;
static char *rawFormat;
static int rawFormatUsed;
static int outNumChan;
static int outNumChanUsed;
static double outFSample;
static int outFSampleUsed;
static int recNumChan;
static int recNumChanUsed;
static double recFSample;
static int recFSampleUsed;
static char *playDevice;
static int playDeviceUsed;
static char *recDevice;
static int recDeviceUsed;
static float maxFsDev;
static int autoConvert;
static int digiConvert;
static float lowerFSample;
static int lowerFSampleUsed;
static float upperFSample;
static int upperFSampleUsed;
static int convert;
static int convertFrac;
static float gain;
static float gain0;
static float highPassFreq;
static int highPassOrder;
static double startTime;
static float duration;
static float forceDuration;
static int forceDurationUsed;
static int stripSilence;
static float quitSilence;
static int silenceThreshold;
static double leadingSilence;
static float trailingSilence;
static float fadeIn;
static float fadeOut;
static int noAudio;
static int wplaxDebug;
static int wplaxList;
static float bufTime;
static float fragTime;
static int fragTimeUsed;
static float playBufTime;
static int ht;
static float al;
static float fc;
static float fd;
static int debugLevel;
static int *varArgIdx;

static CmdLinePara paraList[] = {
  {&varArgIdx,NULL,"<input files (- = stdin) (dflt: audio device)>"},
  {NULL,NULL,NULL}
};

static CmdLineSwitch switchList[] = {
  {"h",NULL,NULL,NULL,NULL,"print help"},
  {"o",&outFileName,"%s",NULL,&outFileNameUsed,
   "output file name (- = stdout) (dflt: audio device)"},
  {"of",&outFileFormat,"%s",NULL,&outFileFormatUsed,
   "output file format (au/wav/aiff/raw/be/le)\n"
   "(16 bit, raw/be: big-endian raw, le: little-endian raw)\n"
   "(dflt: as file extension or au for stdout)"},
  {"if",&inFileFormat,"%s","auto",&inFileFormatUsed,
   "input file format\n"
   "(AFsp: auto/raw/be/le ..., see also -ia -f xxx)\n"
   "(16 bit au/raw: auto/au/raw/be/le, see also -is)\n\b"},
  {"is",&skipHeader,"%d","0",NULL,
   "header bytes to skip if raw input (not AFsp)"},
  {"ia",&stdinAFsp,NULL,NULL,NULL,
   "stdin using AFsp (dflt: 16 bit au/raw)"},
  {"oa",&stdoutAFsp,NULL,NULL,NULL,
   "stdout using AFsp, needs -of xxx\n"
   "(dflt: 16 bit au/raw)"},
  {"f",&rawFormat,"%s",NULL,&rawFormatUsed,
   "raw input file format (AFsp)\n"
   "(dflt: $" RAWENV " / \"" RAWDEFAULT "\")"},
  {"c",&outNumChan,"%d",NULL,&outNumChanUsed,
   "output: number of channels (dflt: as input or 1)"},
  {"s",&outFSample,"%lf",NULL,&outFSampleUsed,
   "output: sampling frequency [Hz] (dflt: as input or 8000.0)"},
  {"rc",&recNumChan,"%d",NULL,&recNumChanUsed,
   "rec: number of channels\n"
   "(SGI: 0 = use audio panel settings) (dflt: as output)"},
  {"rs",&recFSample,"%lf",NULL,&recFSampleUsed,
   "rec: sampling frequency [Hz]\n"
   "(SGI: 0 = use audio panel settings) (dflt: as output)"},
  {"pd",&playDevice,"%s",NULL,&playDeviceUsed,
   "play: audio device name (dflt: default device)"},
  {"rd",&recDevice,"%s",NULL,&recDeviceUsed,
   "rec: audio device name (dflt: default device)"},
  {"df",&maxFsDev,"%f","0.01",NULL,
   "max. tolerated relative sampling frequency deviation\n\b"},
  {"a",&autoConvert,NULL,NULL,NULL,
   "enable automatic conversion to audio device capabilities"},
  {"ad",&digiConvert,NULL,NULL,NULL,
   "enable automatic upsampling to 32/44.1/48 kHz if required\n"
   "(for digital audio device)"},
  {"al",&lowerFSample,"%f",NULL,&lowerFSampleUsed,
   "lower limit for output sampling frequency [Hz] (dflt: none)"},
  {"au",&upperFSample,"%f",NULL,&upperFSampleUsed,
   "upper limit for output sampling frequency [Hz] (dflt: none)"},
  {"x",&convert,NULL,NULL,NULL,
   "enable channel & sample rate conversion"},
  {"xf",&convertFrac,NULL,NULL,NULL,
   "resampling for sub-sample start time accuracy"},
  {"g",&gain,"%f","1.0",NULL,
   "amplitude gain"},
  {"g0",&gain0,"%f","1.0",NULL,
   "add. ampl. gain for chan. 0 (left)"},
  {"hp",&highPassFreq,"%f","0.0",NULL,
   "output high-pass filter frequency [Hz] (0=off)"},
  {"ho",&highPassOrder,"%d","1",NULL,
   "output high-pass filter order"},
  {"ts",&startTime,"%lf","0.0",NULL,
   "start time in input [sec]\n"
   "(use -xf for sub-sample accuracy)"},
  {"td",&duration,"%f","0.0",NULL,
   "max. duration of output [sec] (without added silence)\n"
   "(0=unlimited)"},
  {"tf",&forceDuration,"%f",NULL,&forceDurationUsed,
   "force output duration [sec] (without added silence)\n"
   "(dflt: see -td)"},
  {"ss",&stripSilence,NULL,NULL,NULL,
   "strip leading and trailing silence"},
  {"sq",&quitSilence,"%f","0.0",NULL,
   "trailing silence to quit [sec] (0=off)"},
  {"sm",&silenceThreshold,"%d","0",NULL,
   "leading silence magnitude threshold [LSB]\n"
   "(trailing silence magnitude threshold = 0)"},
  {"sl",&leadingSilence,"%lf","0.0",NULL,
   "add leading silence [sec]"},
  {"st",&trailingSilence,"%f","0.0",NULL,
   "add trailing silence [sec]"},
  {"fi",&fadeIn,"%f","0.0",NULL,
   "signal fade-in [sec]"},
  {"fo",&fadeOut,"%f","0.0",NULL,
   "signal fade-out [sec]"},
  {"n",&noAudio,NULL,NULL,NULL,
   "no audio output (just show audio file format)"},
  {"wd",&wplaxDebug,NULL,NULL,NULL,
   "wplax: print time of played frames on stdout"},
  {"wl",&wplaxList,NULL,NULL,NULL,
   "wplax: list audio file format on stdout (no audio output)"},
  {"b",&bufTime,"%f","0.2",NULL,
   "buffer size [sec] (if no sample rate conv.)"},
  {"bf",&fragTime,"%f",NULL,&fragTimeUsed,
   "fragment size [sec] (recommended if only 2 fragments)"},
  {"bp",&playBufTime,"%f","0.2",NULL,
   "play buffer size [sec] (limit playback latency)\n"
   "(0 disables buffer control to prevent hangup)"},
  {"ht",&ht,"%d","-1",NULL,
   "sample rate conv.: num taps\n"
   "(-1 = auto)"},
  {"al",&al,"%f","-1.0",NULL,
   "sample rate conv.: alpha for Kaiser window\n"
   "(-1 = auto)"},
  {"fc",&fc,"%f","-1.0",NULL,
   "sample rate conv.: 6 dB cutoff freq / input bandwidth\n"
   "(-1 = auto)"},
  {"fd",&fd,"%f","-1.0",NULL,
   "sample rate conv.: 100 dB cutoff freq / input bandwidth\n"
   "(-1 = auto)"},
  {"d",&debugLevel,"%i","0",NULL,
   "debug level (on stderr) (0=off 1=basic 2=med 3=high)\n"
   "(+4*austreamDbgLvl +16*playrecDbgLvl +64*rateconvDbgLvl)\n\b"},
  {NULL,NULL,NULL,NULL,NULL,NULL}
};

static int stop = 0;


/* ---------- local functions ---------- */

#ifndef MSVC
void stopproc (int sig)
{
  stop = 1;
  if (sig==SIGPIPE) {
    stop = 2;
    signal(SIGPIPE,stopproc);	/* catch broken output pipe -- again */
  }
  /* 2nd SIGINT will abort immediately */
}
#endif


int isfmtstr (char *filename, char *fmtstr)
/* isfmtstr returns true if filename has extension fmtstr */
{
  int i;

  i = strlen(filename)-strlen(fmtstr);
  if (i<0)
    return 0;
  filename += i;
  while (*filename) {
    if (tolower(*filename) != *fmtstr)
      return 0;
    filename++;
    fmtstr++;
  }
  return 1;
}


/* ---------- main ---------- */

int main (int argc, char *argv[])
{
  int listIdx;
  char inFileName[STRLEN];
  char fileName[STRLEN];
#ifdef USE_AFSP
  AFILE *inaf,*outaf;
  FILE *tf;
#endif
  AuStream *inas,*outas;
  PRStream *inpr,*outpr;
  RCBuf *rc;
  int result;
  int RCdebugLevel;

  long maxBufLen;
  long bufLen;
  long outBufLen;
  long convLen;
  long bufTrans;
  long inBufUsed;
  long outBufUsed;
  long tmpBufUsed;
  long inBufStart;
  long inBufEnd;
  long numClip;
  long totClip;
  short *bufS;
  float *bufF;
  short *outBufS;
  float *outBufF;
  long tmpBufEnd;
  float *tmpBufF;
  long transBufEnd;
  float *transBufF;
  float fade;
  long i,tmp;
  int absPeak;
  char level[17];

  int state;
  int inNumChan;
#ifdef USE_AFSP
  long inNumChanL;
#endif
  float inFSampleF;
  double inFSample;
  long fileNumSamp;
  long inNumSamp;
  long outNumSamp;
  int devNumChan;
  float devFSample;
  int devFragsize;
  int devFragstotal;
  long devLatency;
  long startNumSamp;
  long durNumSamp;
  long quitNumSamp;
  long inStart;
  long outStart;
  long outEnd;
  long outEndKnown;
  long leadingNumSamp;
  long trailingNumSamp;
  long fadeInNumSamp;
  long fadeOutNumSamp;
  long stripNumSamp;
  long stripFrame;
  long leadingStripNumSamp;
  long trailingStripNumSamp;
  int actOutNumChan;
  int actConvert;
  double actOutFSample;
  float **hpfState;
  int j,k;
  float hpfTmp,hpfB;
  double fracOff;
  double actStartTime;
  float actDuration;
  double actLeadingSilence;

#ifdef USE_AFSP
  int fmt;
  int fmti;
  struct {
    char *str;
    int fmt;
  } fmtstr[] = {
    {"au",FTW_AU*256},
    {"snd",FTW_AU*256},
    {"wav",FTW_WAVE*256},
    {"wave",FTW_WAVE*256},
    {"aif",FTW_AIFF_C*256},
    {"aiff",FTW_AIFF_C*256},
    {"aifc",FTW_AIFF_C*256},
    {"raw",FTW_NH_EB*256},	/* no header big-endian */
    {"be",FTW_NH_EB*256},	/* no header big-endian */
    {"le",FTW_NH_EL*256},	/* no header little-endian */
    {NULL,-1}
  };
#endif

  int outfmt;
  int infmt;
  struct {
    char *str;
    int fmt;
  } aufmtstr[] = {
    {"auto",0},
    {"au",0},
    {"snd",0},
    {"raw",1},			/* no header big-endian */
    {"be",1},			/* no header big-endian */
    {"le",2},			/* no header little-endian */
    {NULL,-1}
  };

  typedef enum {AUDEV, AUSTREAM, AUFILE} autype;
  autype intype,outtype;

  /* evaluate command line  */
  CmdLineInit(0);
  result = CmdLineEval(argc,argv,paraList,switchList,1,&progName);
  if (result) {
    if (result==1) {
      fprintf(stderr,"%s: %s\n"
	      "%s\n"
#ifdef USE_AFSP
	      "all AFsp file formats supported\n"
#else
	      "only 16 bit au/raw format supported\n"
#endif
	      ,progName,PROGVER "   " DISTVER,CVSID);
      CmdLineHelp(progName,paraList,switchList,stderr);
      exit (1);
    }
    else
      CommonExit(1,"command line error (\"-h\" for help)");
  }
  AuInit((debugLevel>>2)&3);
  PRInit((debugLevel>>4)&3);
  RCdebugLevel = (debugLevel>>6)&3;
  debugLevel &= 3;

  if (debugLevel)
    fprintf(stderr,"%s: %s\n",progName,PROGVER "   " DISTVER);
  if (debugLevel>1) {
    fprintf(stderr,"%s\n",CVSID);
    fprintf(stderr,
#ifdef USE_AFSP
	    "all AFsp file formats supported, "
#else
	    "only 16 bit au/raw format supported, "
#endif
	    "debugLevel=%d\n",debugLevel);
  }

#ifdef USE_AFSP
  if (rawFormatUsed)
    AFsetNHpar(rawFormat);
  else {
    if (getenv(RAWENV))
      AFsetNHpar("$" RAWENV);
    else
      AFsetNHpar(RAWDEFAULT);
  }
#endif

  if (!recDeviceUsed)
    recDevice = NULL;
  if (!playDeviceUsed)
    playDevice = NULL;

  if (!outFileNameUsed && varArgIdx[0]<0) {
    CommonWarning("nothing to play -> just resetting audio device ...");
    devNumChan = outNumChan = (outNumChanUsed)?outNumChan:1;
    outFSample = (outFSampleUsed)?(float)outFSample:8000.0f;
    devFSample = (float)outFSample;
    if (!(outpr=PROpenWrite(playDevice,&devNumChan,&devFSample)))
      CommonExit(1,"can not open output audio device");
    /* check whether sampling frequencies are similar enough */
    if (fabs(devFSample-outFSample) < maxFsDev*outFSample)
      devFSample = (float)outFSample;
    if (devNumChan != outNumChan || devFSample != outFSample)
      CommonWarning("can not configure output audio device as requested");
    PRClose(outpr);
    exit (0);
  }
  
#ifndef USE_AFSP
  if (stdinAFsp || stdoutAFsp)
    CommonExit(1,"not linked with AFsp");
#endif

  if (convertFrac)
    convert = 1;
  if (outFileNameUsed && varArgIdx[0]>=0 && varArgIdx[1]>=0)
    CommonExit(1,"max one input file allowed when using -o");

  if (stripSilence && forceDurationUsed)
    CommonWarning("stripping only leading silence because -tf is specified");
  if (forceDurationUsed && duration!=0)
    CommonWarning("ignoring -td because -tf is specified");
  if (forceDurationUsed)
    duration = forceDuration;
  if (duration < 0)
    duration = 0;
  if (startTime+duration < 0) {
    startTime = -duration;
    forceDurationUsed = 1;
    CommonWarning("start time + duration < 0 leaves only silence");
  }
  if (quitSilence < 0)
    quitSilence = 0;
  if (leadingSilence < 0)
    leadingSilence = 0;
  if (trailingSilence < 0)
    trailingSilence = 0;
  if (fadeIn < 0)
    fadeIn = 0;
  if (fadeOut < 0)
    fadeOut = 0;
  if (fragTimeUsed && fragTime <= 0)
    fragTime = 0.040;
  devFragsize = 0;
  devFragstotal = 0;

#ifndef WIN32
  signal(SIGINT,stopproc);	/* catch ^C */
  signal(SIGPIPE,stopproc);	/* catch broken output pipe */
#endif

  /* process all files on command line or record from audio device */
  listIdx = 0;
  while (!stop && (outFileNameUsed || varArgIdx[listIdx] >= 0)) {

    if (debugLevel)
      fprintf(stderr,
	      "%s: %s -> %s\n",progName,
	      (varArgIdx[listIdx]>=0)?argv[varArgIdx[listIdx]]:"audio device",
	      (outFileNameUsed)?outFileName:"audio device");

    /* open input */
    if (varArgIdx[listIdx]>=0) {
      if (ComposeFileName(argv[varArgIdx[listIdx]],
			  0,NULL,NULL,inFileName,STRLEN))
	CommonExit(1,"composed input file name too long \"%s\"",argv[listIdx]);
      if (strcmp(inFileName,"-")==0 && !stdinAFsp) {
	intype = AUSTREAM;
	infmt = 0;
	while (aufmtstr[infmt].str &&
	       !isfmtstr(inFileFormat,aufmtstr[infmt].str))
	  infmt++;
	if (!aufmtstr[infmt].str)
	  CommonExit(1,"unkown input audio format");
	if (infmt && !(outNumChanUsed && outFSampleUsed))
	  CommonExit(1,"for raw input -c and -s required");
	if (!(inas=AuOpenReadNB(inFileName,0,aufmtstr[infmt].fmt,skipHeader,
				&inNumChan,&inFSampleF,&fileNumSamp,NULL)))
	  CommonExit(1,"can not open input \"%s\"",inFileName);
	inFSample = inFSampleF;
      }
      else {
#ifdef USE_AFSP
	intype = AUFILE;
	if (inFileFormatUsed) {
	  if (!strcmp(inFileFormat,"be")) {
	    AFsetNHpar("integer16, 0, 8000., big-endian, 1, 1.0");
	    inFileFormat = "noheader";
	  }
	  if (!strcmp(inFileFormat,"le")) {
	    AFsetNHpar("integer16, 0, 8000., little-endian, 1, 1.0");
	    inFileFormat = "noheader";
	  }
	  if (AFsetFileType(strcmp(inFileFormat,"raw")?
			    inFileFormat:"noheader"))
	    CommonExit(1,"can not set input format \"%s\"",inFileFormat);
	}
	if (strcmp(inFileName,"-")) {
	  if (!(tf=fopen(inFileName,"rb")))
	    /* file not found, AFopenRead() would exit ... */
	    CommonExit(1,"can not open input \"%s\"",inFileName);
	  fclose(tf);
	}
	if (!(inaf=AFopenRead(inFileName,&fileNumSamp,&inNumChanL,
			      &inFSampleF,
			      (debugLevel>1)?stderr:(FILE*)NULL)))
	  CommonExit(1,"can not open input \"%s\"",inFileName);
	inNumChan = inNumChanL;
#else
	intype = AUSTREAM;
	infmt = 0;
	while (aufmtstr[infmt].str &&
	       !isfmtstr(inFileFormat,aufmtstr[infmt].str))
	  infmt++;
	if (!aufmtstr[infmt].str)
	  CommonExit(1,"unkown input audio format");
	if (infmt && !(outNumChanUsed && outFSampleUsed))
	  CommonExit(1,"for raw input -c and -s required");
	if (!(inas=AuOpenReadNB(inFileName,0,aufmtstr[infmt].fmt,skipHeader,
				&inNumChan,&inFSampleF,&fileNumSamp,NULL)))
	  CommonExit(1,"can not open input \"%s\"",inFileName);
#endif
	inFSample = inFSampleF;
      }
      if (!convert) {
	if (outNumChanUsed)
	  inNumChan = outNumChan;
	if (outFSampleUsed)
	  inFSample = outFSample;
      }
    }
    else {
      intype = AUDEV;
      fileNumSamp = -1;
      inNumChan = (outNumChanUsed)?outNumChan:1;
      inFSample = (outFSampleUsed)?outFSample:8000.0f;
      if (recNumChanUsed)
	inNumChan = recNumChan;
      if (recFSampleUsed)
	inFSample = recFSample;
      devNumChan = inNumChan;
      devFSample = (float)inFSample;
      if (fragTimeUsed) {
	devFragsize = (int)(fragTime*devFSample+.5)*devNumChan;
	devFragstotal = 0x7fff;
      }
      if (!(inpr=PROpenReadFrag(recDevice,&devNumChan,&devFSample,
				&devFragsize,&devFragstotal)))
	CommonExit(1,"can not open input audio device");
      if (debugLevel>1)
	fprintf(stderr,
		"rec: numChan=%d fSample=%.1f fragsize=%d fragstotal=%d "
		"(%5.3f/%5.3f s)\n",
		devNumChan,devFSample,devFragsize,devFragstotal,
		(devFragsize/devNumChan)/devFSample,
		((devFragstotal*devFragsize)/devNumChan)/devFSample);
      if (devNumChan<=0 && outNumChanUsed)
	devNumChan = inNumChan = outNumChan;
      if (devFSample<=0 && outFSampleUsed) {
	inFSample = (float)outFSample;
	devFSample = (float)inFSample;
      }
      if (devNumChan<=0 || devFSample<=0)
	CommonExit(1,"input audio device configuration troubles");
      /* check whether sampling frequencies are similar enough */
      if (fabs(devFSample-inFSample) < maxFsDev*inFSample)
	devFSample = (float)inFSample;
      if (devNumChan != inNumChan || devFSample != inFSample) {
	CommonWarning("can not configure output audio device as requested");
	CommonWarning("=> using %dch/%.0fHz instead of %dch/%.0fHz",
		      devNumChan,devFSample,inNumChan,inFSample);
      }
      inNumChan = devNumChan;
      inFSample = devFSample;
    }
    if (debugLevel>1)
      fprintf(stderr,
	      "input=%s inNumChan=%d inFSample=%.1f fileNumSamp=%ld\n",
	      (intype==AUDEV)?"audio device":inFileName,
	      inNumChan,inFSample,fileNumSamp);
    
    if (noAudio)
      fprintf(stderr,
	      "%8.3f s   %d ch   %7.1f Hz   %s\n",
	      fileNumSamp/(inFSample*inNumChan),inNumChan,inFSample,
	      (intype==AUDEV)?"audio device":inFileName);

    if (wplaxList)
      fprintf(stdout,
	      "%8.3f s   %d ch   %7.1f Hz   %s\n",
	      fileNumSamp/(inFSample*inNumChan),inNumChan,inFSample,
	      (intype==AUDEV)?"audio device":inFileName);

    if (!(noAudio || wplaxList)) {

      /* open output */
      if (!outNumChanUsed)
	outNumChan = inNumChan;
      if (!outFSampleUsed)
	outFSample = inFSample;
      actOutNumChan = outNumChan;
      actOutFSample = outFSample;
      actConvert = convert;

      if (outFileNameUsed) {
	if (ComposeFileName(outFileName,0, NULL,NULL,fileName,STRLEN))
	  CommonExit(1,"composed output file name too long \"%s\"",
		     outFileName);
	if (strcmp(fileName,"-")==0 && !stdoutAFsp) {
	  outtype = AUSTREAM;
	  outfmt = 0;
	  if (outFileFormatUsed) {
	    while (aufmtstr[outfmt].str &&
		   !isfmtstr(outFileFormat,aufmtstr[outfmt].str))
	      outfmt++;
	    if (!aufmtstr[outfmt].str)
	      CommonExit(1,"unkown output audio format");
	  }
	  if (!(outas=AuOpenWriteNB(fileName,0,aufmtstr[outfmt].fmt,
				    actOutNumChan,(float)actOutFSample,NULL)))
	    CommonExit(1,"can not open output \"%s\"",fileName);
	}
	else {
	  if (!outFileFormatUsed)
	    outFileFormat = fileName;
#ifdef USE_AFSP
	  outtype = AUFILE;
	  fmti = 0;
	  while (fmtstr[fmti].str && !isfmtstr(outFileFormat,fmtstr[fmti].str))
	    fmti++;
	  if (fmtstr[fmti].str)
	    fmt = FD_INT16 + fmtstr[fmti].fmt;
	  else
	    CommonExit(1,"unkown output audio format");
	  if (!(outaf=AFopenWrite(fileName,fmt,actOutNumChan,actOutFSample,
				  (debugLevel>1)?stderr:(FILE*)NULL)))
	    CommonExit(1,"can not open output \"%s\"",fileName);
#else
	  outtype = AUSTREAM;
	  outfmt = 0;
	  while (aufmtstr[outfmt].str &&
		 !isfmtstr(outFileFormat,aufmtstr[outfmt].str))
	    outfmt++;
	  if (!aufmtstr[outfmt].str)
	    CommonExit(1,"unkown output audio format");
	  if (!(outas=AuOpenWriteNB(fileName,0,aufmtstr[outfmt].fmt,
				    actOutNumChan,actOutFSample,NULL)))
	    CommonExit(1,"can not open output \"%s\"",fileName);
#endif
	}
      }
      else {
	outtype = AUDEV;
	devNumChan = actOutNumChan;
	devFSample = (float)actOutFSample;
	if (digiConvert) {
	  if (devFSample*(1-maxFsDev) < 32000.) {
	    actConvert = 1;
	    actOutFSample = 32000.;
	    devFSample = (float)actOutFSample;
	  }
	  else if (devFSample*(1-maxFsDev) < 44100.) {
	    actConvert = 1;
	    actOutFSample = 44100.;
	    devFSample = (float)actOutFSample;
	  }
	  else if (devFSample*(1-maxFsDev) < 48000.) {
	    actConvert = 1;
	    actOutFSample = 48000.;
	    devFSample = (float)actOutFSample;
	  }
	}
	if (lowerFSampleUsed && devFSample*(1+maxFsDev) < lowerFSample) {
	  actConvert = 1;
	  actOutFSample = lowerFSample;
	  devFSample = (float)actOutFSample;
	}
	if (upperFSampleUsed && devFSample*(1-maxFsDev) > upperFSample) {
	  actConvert = 1;
	  actOutFSample = upperFSample;
	  devFSample = (float)actOutFSample;
	}
	if (fragTimeUsed) {
	  devFragsize = (int)(fragTime*devFSample+.5)*devNumChan;
	  devFragstotal = 0x7fff;
	}
	if (!(outpr=PROpenWriteFrag(playDevice,&devNumChan,&devFSample,
				    &devFragsize,&devFragstotal)))
	  CommonExit(1,"can not open output audio device");
	if (debugLevel>1)
	  fprintf(stderr,
		  "play: numChan=%d fSample=%.1f fragsize=%d fragstotal=%d "
		  "(%5.3f/%5.3f s)\n",
		  devNumChan,devFSample,devFragsize,devFragstotal,
		  (devFragsize/devNumChan)/devFSample,
		  ((devFragstotal*devFragsize)/devNumChan)/devFSample);
	/* check whether sampling frequencies are similar enough */
	if (fabs(devFSample-actOutFSample) < maxFsDev*actOutFSample)
	  devFSample = (float)actOutFSample;
	if (devNumChan != actOutNumChan || devFSample != actOutFSample) {
	  CommonWarning("can not configure output audio device as requested");
	  if (autoConvert) {
	    actConvert = 1;
	    actOutNumChan = devNumChan;
	    actOutFSample = devFSample;
	    CommonWarning("=> converting from %dch/%.0fHz to %dch/%.0fHz",
			  inNumChan,inFSample,actOutNumChan,actOutFSample);
	  }
	  else
	    CommonWarning("=> using %dch/%.0fHz instead of %dch/%.0fHz",
			  devNumChan,devFSample,actOutNumChan,actOutFSample);
	}
	if (playBufTime>0) {
	  devLatency = (long)(devNumChan*devFSample*playBufTime+.5);
	  if (devLatency < devFragsize)
	    CommonWarning("playback latency (%5.3f s) < "
			  "fragment size (%5.3f s) could block!",
			  (devLatency/devNumChan)/devFSample,
			  (devFragsize/devNumChan)/devFSample);
	  PRWriteBuffer(outpr,devLatency);
	}
      }
      if (debugLevel>1)
	fprintf(stderr,"output=%s actOutNumChan=%d actOutFSample=%.1f\n",
		(outtype==AUDEV)?"audio device":fileName,
		actOutNumChan,actOutFSample);

      /* init high-pass filter */
      if (highPassFreq>0) {
	if (highPassOrder<0)
	  CommonExit(-1,"high-pass filter order negative");
	if (!(hpfState=(float**)malloc(highPassOrder*sizeof(float*))) ||
	    !(hpfState[0]=(float*)malloc(highPassOrder*actOutNumChan*
					 sizeof(float))))
	  CommonExit(-1,"failed to allocate *hpfState");
	for (k=1; k<highPassOrder; k++)
	  hpfState[k] = hpfState[0]+k*actOutNumChan;
	hpfB = (float)(1-(highPassFreq/actOutFSample*2*PI)*
	  sqrt(pow(2,1.0/highPassOrder)-1));
	if (hpfB<0)
	  CommonExit(-1,"high-pass filter freq. too high");
	for (k=0; k<highPassOrder; k++)
	  for (i=0; i<actOutNumChan; i++)
	    hpfState[k][i] = 0;
      }

      /* init buffers & conversion */
      quitNumSamp = inNumChan*(long)(inFSample*quitSilence+.5);
      trailingNumSamp = actOutNumChan*(long)(actOutFSample*trailingSilence+.5);
      fadeInNumSamp = actOutNumChan*(long)(actOutFSample*fadeIn+.5);
      fadeOutNumSamp = actOutNumChan*(long)(actOutFSample*fadeOut+.5);
      if (startTime < 0) {
	if (duration==0 && !forceDurationUsed)
	  actDuration = 0;
	else
	  actDuration = (float)(duration+startTime);
	actLeadingSilence = leadingSilence-startTime;
	if (convertFrac) {
	  leadingNumSamp = actOutNumChan*
	    (long)(actOutFSample*actLeadingSilence+1.0);
	  actStartTime = (leadingNumSamp/actOutNumChan/actOutFSample)-
	    actLeadingSilence;
	}
	else {
	  actStartTime = 0;
	  leadingNumSamp = actOutNumChan*
	    (long)(actOutFSample*actLeadingSilence+.5);
	}
      }
      else {
	actStartTime = startTime;
	actDuration = duration;
	leadingNumSamp = actOutNumChan*(long)(actOutFSample*leadingSilence+.5);
      }
      if (convertFrac) {
	startNumSamp = inNumChan*(long)(inFSample*actStartTime);
	fracOff = inFSample*actStartTime-(double)(startNumSamp/inNumChan);
      }
      else {
	startNumSamp = inNumChan*(long)(inFSample*actStartTime+.5);
	fracOff = 0.0;
      }
      durNumSamp = inNumChan*(long)(inFSample*actDuration+.5);
      if (fileNumSamp>=0 && startNumSamp>fileNumSamp) {
	startNumSamp = fileNumSamp;
	if (forceDurationUsed) {
	  trailingNumSamp = actOutNumChan*
	    (long)(actOutFSample*
		   (trailingSilence+
		    (startNumSamp-fileNumSamp)/inNumChan/inFSample)+.5);
	  CommonWarning("input signal too short -> leaving only silence");
	}
	else
	  CommonWarning("input signal too short -> limiting start time");
      }
      if (durNumSamp && fileNumSamp>=0 &&
	  durNumSamp+startNumSamp>fileNumSamp && !forceDurationUsed) {
	durNumSamp = 0;
	CommonWarning("input signal too short -> limiting duration");
      }
      if (durNumSamp==0 && fileNumSamp>=0 && !stripSilence &&
	  !forceDurationUsed)
	durNumSamp = fileNumSamp-startNumSamp;

      if (actConvert && inNumChan!=actOutNumChan &&
	  inNumChan>1 && actOutNumChan>1)
	CommonExit(-1,"down-/upmixing only to/from mono");
      bufLen = (long)(inNumChan*inFSample*bufTime+.5);
      bufLen = max(bufLen,inNumChan);
      bufLen = (bufLen/inNumChan)*inNumChan;
      rc = NULL;
      if (actConvert &&
	  (inFSample!=actOutFSample || fc>=0 || fd>=0 || convertFrac)) {
	if (!(rc=RateConvInit(RCdebugLevel,
			      (double)actOutFSample/(double)inFSample,fracOff,
			      min(inNumChan,actOutNumChan),ht,al,fc,fd,
			      &convLen)))
	  CommonExit(1,"can not init sample rate conversion");
	bufLen = convLen;
	if (inNumChan>1 && actOutNumChan==1)
	  bufLen *= inNumChan;
      }
      outBufLen = actOutNumChan*(long)((bufLen/inNumChan+2)/inFSample*
				       actOutFSample+.5+2);
      /* HP20011113 added +2 for safe worst-case output buffer size */
      maxBufLen = max(bufLen,outBufLen);
      if (debugLevel>2)
	fprintf(stderr,"bufLen=%ld outBufLen=%ld maxBufLen=%ld\n",
		bufLen,outBufLen,maxBufLen);
      
      if (!(bufF = (float *)malloc(maxBufLen*sizeof(float))) ||
	  !(tmpBufF = (float *)malloc(maxBufLen*sizeof(float))) ||
	  !(bufS = (short *)malloc(maxBufLen*sizeof(short)))) {
	CommonExit(1,"failed to allocate bufS/tmpBufF/bufS");
      }
      outBufS = bufS;

      inNumSamp = 0;
      outNumSamp = 0;
      totClip = 0;
      inBufUsed = 0;
      inBufEnd = 0;
      inStart = 0;
      outStart = leadingNumSamp;
      outEnd = outStart;
      trailingStripNumSamp = 0;
      stripFrame = 0;
      if (durNumSamp || forceDurationUsed)
	outEndKnown = outStart+actOutNumChan*
	  (long)(durNumSamp/inNumChan/inFSample*actOutFSample+.5);
      else {
	outEndKnown = 0;
	if (fadeOutNumSamp)
	  CommonWarning("unkown signal duration -> fade out disabled");
      }

      if (debugLevel)
	fprintf(stderr,"%8s %8s %16s  %8.3f s  %d ch  %7.1f Hz %s\n",
		"in   [s]","out  [s]","-96dB........0dB",
		fileNumSamp/(inFSample*inNumChan),inNumChan,inFSample,
		(intype==AUDEV)?"audio device":inFileName);

      /* state: */
      /* 1 = skip leading silence */
      /* 2 = skip start time */
      /* 3 = add leading silence */
      /* 4 = fade in / copy / fade out */
      /* 5 = add trailing silence */
      /* 6 = stop */
      /* 7 = skip trailing silence (reading) */
      /* 8 = skip trailing silence (writing) */

      state = (stripSilence)?1:2;

      do {
	if (debugLevel) {
	  if (debugLevel<=2) {
	    fprintf(stderr,"\r%8.3f %8.3f ",inNumSamp/inNumChan/inFSample,
		    outNumSamp/actOutNumChan/actOutFSample);
	    fflush(stderr);
	  }
	  else
	    fprintf(stderr,"%8.3f %8.3f ",inNumSamp/inNumChan/inFSample,
		    outNumSamp/actOutNumChan/actOutFSample);
	}
	if (wplaxDebug && state==4) {
	  fprintf(stdout,"%8.3f\n",inNumSamp/inNumChan/inFSample);
	  fflush(stdout);
	}

	/* prepare reading */
	switch (state) {
	case 1:
	  bufTrans = bufLen;
	  break;
	case 2:
	  bufTrans = inStart+startNumSamp-inNumSamp;
	  break;
	case 4:
	case 7:
	  bufTrans = (durNumSamp || forceDurationUsed)?
	    inStart+startNumSamp+durNumSamp-inNumSamp:bufLen;
	  break;
	default:
	  bufTrans = 0;
	  break;
	}
	if (fragTimeUsed && !actConvert &&
	    devFragsize>0 && bufTrans>devFragsize)
	  bufTrans = devFragsize;
	if (bufTrans>bufLen)
	  bufTrans = bufLen;
	if (bufTrans<0)
	  bufTrans = 0;

	/* read samples */
	if (inBufEnd) {
	  if (inBufEnd==inBufUsed)
	    inBufUsed = 0;
	  else {
	    for (i=inBufEnd; i<inBufUsed; i++)
	      bufF[i-inBufEnd] = bufF[i];
	    inBufUsed -= inBufEnd;
	  }
	}
	if (bufTrans>inBufUsed) {
	  inBufStart = inBufUsed;
	  switch (intype) {
	  case AUSTREAM:
	    inBufUsed += AuReadData(inas,bufS+inBufStart,bufTrans-inBufStart);
	    for (i=inBufStart; i<inBufUsed; i++)
	      bufF[i] = bufS[i];
	    break;
	  case AUFILE:
#ifdef USE_AFSP
	    if (state==2)
	      inBufUsed = bufTrans-inBufStart;   /* skip to start time */
	    else
	      inBufUsed += AFreadData(inaf,inNumSamp+inBufStart,
				      bufF+inBufStart,bufTrans-inBufStart);
#endif
	    break;
	  case AUDEV:
	    if (PRReadData(inpr,bufS+inBufStart,bufTrans-inBufStart))
	      stop = 1;
	    else
	      inBufUsed += bufTrans-inBufStart;
	    for (i=inBufStart; i<inBufUsed; i++)
	      bufF[i] = bufS[i];
	    break;
	  }
	}

	if (debugLevel>2)
	  fprintf(stderr,
		  "\ns=%d bufTra=%ld inBE=%ld inBU=%ld strf=%ld\n",
		  state,bufTrans,inBufEnd,inBufUsed,stripFrame);

	/* strip silence */
	switch (state) {
	case 1:
	  i = 0;
	  while (i<inBufUsed && bufF[i]<silenceThreshold+.5 &&
		 bufF[i]>-silenceThreshold-.5)
	    i++;
	  stripNumSamp = inNumChan*(i/inNumChan);
	  inBufEnd = stripNumSamp;
	  transBufEnd = 0;
	  break;
	case 4:
	  inBufEnd = min(bufTrans,inBufUsed);
	  transBufF = bufF;
	  transBufEnd = inBufEnd;
	  stripNumSamp = 0;
	  if (!stripSilence || !inBufEnd)
	    break;
	  i = inBufEnd-1;
	  while (i>=0 && bufF[i]<+.5 &&
		 bufF[i]>-.5)
	    i--;
	  stripNumSamp = inBufEnd-inNumChan*(i/inNumChan+1);
	  if (stripNumSamp && !forceDurationUsed) {
	    if ((quitNumSamp && stripNumSamp>=quitNumSamp) ||
		inBufEnd<bufLen) {
	      transBufEnd = inBufEnd-stripNumSamp;
	      trailingStripNumSamp = stripNumSamp;
	      durNumSamp = inNumSamp+transBufEnd-
		inStart-startNumSamp;
	      break;
	    }
	    tmpBufEnd = inBufEnd-stripNumSamp;
	    for (i=0; i<tmpBufEnd; i++)
	      tmpBufF[i] = bufF[i];
	    state = 7;
	  }
	  break;
	case 7:
	  inBufEnd = min(bufTrans,inBufUsed);
	  i = 0;
	  while (i<inBufEnd && bufF[i]<+.5 &&
		 bufF[i]>-.5)
	    i++;
	  stripNumSamp += inNumChan*(i/inNumChan);
	  if ((quitNumSamp && stripNumSamp>=quitNumSamp) ||
	      (i==inBufEnd && inBufEnd<bufTrans)) {
	    trailingStripNumSamp = stripNumSamp;
	    transBufEnd = tmpBufEnd;
	    transBufF = tmpBufF;
	    durNumSamp = inNumSamp+inBufEnd-
	      inStart-startNumSamp-stripNumSamp;
	    state = 4;
	    break;
	  }
	  if (i==inBufEnd) {
	    stripFrame++;
	    break;
	  }
	  transBufF = tmpBufF;
	  transBufEnd = bufLen;
	  for (i=tmpBufEnd; i<transBufEnd; i++)
	    transBufF[i] = 0;
	  inBufEnd = 0;
	  state = 8;
	  break;
	case 8:
	  if (stripFrame) {
	    transBufF = tmpBufF;
	    transBufEnd = bufLen;
	    for (i=0; i<transBufEnd; i++)
	      transBufF[i] = 0;
	    stripFrame--;
	    break;
	  }
	  inBufEnd = min(bufLen,inBufUsed);
	  transBufF = bufF;
	  transBufEnd = inBufEnd;
	  stripNumSamp = 0;
	  state = 4;
	  if (!stripSilence || !inBufEnd)
	    break;
	  i = inBufEnd-1;
	  while (i>=0 && bufF[i]<+.5 &&
		 bufF[i]>-.5)
	    i--;
	  stripNumSamp = inBufEnd-inNumChan*(i/inNumChan+1);
	  if (stripNumSamp) {
	    if ((quitNumSamp && stripNumSamp>=quitNumSamp) ||
		inBufEnd<bufLen) {
	      transBufEnd = inBufEnd-stripNumSamp;
	      trailingStripNumSamp = stripNumSamp;
	      durNumSamp = inNumSamp+transBufEnd-
		inStart-startNumSamp;
	      break;
	    }
	    tmpBufEnd = inBufEnd-stripNumSamp;
	    for (i=0; i<tmpBufEnd; i++)
	      tmpBufF[i] = bufF[i];
	    state = 7;
	  }
	  break;
	default:
	  inBufEnd = min(bufTrans,inBufUsed);
	  transBufF = bufF;
	  transBufEnd = inBufEnd;
	  break;
	}
	inNumSamp += inBufEnd;

	/* prepare writing */
	switch (state) {
	case 3:
	  outBufUsed = leadingNumSamp-outNumSamp;
	  if (outBufUsed>outBufLen)
	    outBufUsed = outBufLen;
	  outBufF = tmpBufF;
	  for (i=0; i<outBufUsed; i++)
	    outBufF[i] = 0;
	  break;
	case 4:
	case 8:
	  outBufUsed = transBufEnd;
	  outBufF = transBufF;
	  if (actConvert) {
	    if (inNumChan>1 && actOutNumChan==1) {
	      outBufUsed = transBufEnd/inNumChan;
	      for (i=0; i<outBufUsed; i++) {
		transBufF[i] = transBufF[inNumChan*i];
		for (j=1; j<inNumChan; j++)
		  transBufF[i] += transBufF[inNumChan*i+j];
		transBufF[i] /= inNumChan;
	      }
	    }
	    if (rc) {
	      while (outBufUsed<convLen)
		transBufF[outBufUsed++] = 0;
	      outBufUsed = RateConv(rc,transBufF,outBufUsed,&outBufF);
	      if (durNumSamp) {
		outEnd = outStart+actOutNumChan*
		  (long)(durNumSamp/inNumChan/inFSample*actOutFSample+.5);
		tmpBufUsed = outEnd-outNumSamp;
		if (actOutNumChan>1) {
		  if (inNumChan==1)
		    tmpBufUsed /= actOutNumChan;
		  else
		    tmpBufUsed = (tmpBufUsed/actOutNumChan)*actOutNumChan;
		}
		if (outBufUsed>tmpBufUsed)
		  outBufUsed = tmpBufUsed;
	      }
	    }
	    if (inNumChan==1 && actOutNumChan>1) {
	      outBufUsed *= actOutNumChan;
	      for (i=outBufUsed-1; i>=0; i--)
		transBufF[i] = outBufF[i/actOutNumChan];
	      outBufF = transBufF;
	    }
	  }
	  break;
	case 5:
	  outBufUsed = outEnd+trailingNumSamp-outNumSamp;
	  if (outBufUsed<0)
	    outBufUsed = 0;
	  if (outBufUsed>outBufLen)
	    outBufUsed = outBufLen;
	  outBufF = bufF;
	  for (i=0; i<outBufUsed; i++)
	    outBufF[i] = 0;
	  break;
	default:
	  outBufUsed = 0;
	  break;
	}
	if (outBufUsed>maxBufLen)
	  CommonExit(-1,"output buffer size troubles");

	/* high-pass filter output samples */
	if (outBufUsed && highPassFreq>0) {
	  for (i=0; i<outBufUsed; i++) {
	    j = i%actOutNumChan;
	    for (k=0; k<highPassOrder; k++) {
	      hpfTmp = hpfState[k][j];
	      hpfState[k][j] = outBufF[i] + hpfB*hpfState[k][j];
	      outBufF[i] = hpfState[k][j] - hpfTmp;
	    }
	  }
	}

	/* fade in/out & clipping */
	numClip = 0;
	absPeak = 0;
	for (i=0; i<outBufUsed; i++) {
	  fade = 1;
	  if (fadeInNumSamp && outNumSamp+i-outStart<fadeInNumSamp)
	    fade *= (float)(((outNumSamp+i-outStart)/actOutNumChan)/
	      (fadeInNumSamp/actOutNumChan+.0));
	  if (fadeOutNumSamp && outEndKnown>0 &&
	      outNumSamp+i-outEndKnown>-fadeOutNumSamp)
	    fade *= (float)(((outNumSamp+i-outEndKnown)/actOutNumChan)/
	      (-fadeOutNumSamp/actOutNumChan+.0));
	  if (i%actOutNumChan==0)
	    fade *= gain0;
	  tmp = ((long)(gain*fade*outBufF[i]+32768.5))-32768;
	  if (tmp>32767) {
	    tmp = 32767;
	    numClip++;
	  }
	  if (tmp<-32768) {
	    tmp = -32768;
	    numClip++;
	  }
	  if (absPeak < tmp)
	    absPeak = tmp;
	  if (absPeak < -tmp)
	    absPeak = -tmp;
	  outBufS[i] = (short)tmp;
	  outBufF[i] = (short)tmp;	/* for AFsp output */
	}
	totClip += numClip;

	if (debugLevel) {
	  for (i=0; i<16; i++) {
	    level[i] = (absPeak)?'*':'-';
	    absPeak >>= 1;
	  }
	  level[16]='\0';
	  fprintf(stderr,"%s ",level);
	  if (debugLevel<=2)
	    fflush(stderr);
	}

	if (debugLevel>2)
	  fprintf(stderr,
		  "\ns=%d bufTra=%ld inBE=%ld traBE=%ld "
		  "stripNS=%ld durNS=%ld outBU=%ld numC=%ld\n",
		  state,bufTrans,inBufEnd,transBufEnd,
		  stripNumSamp,durNumSamp,outBufUsed,numClip);

	/* write samples */
	if (outBufUsed)
	  switch (outtype) {
	  case AUSTREAM:
	    AuWriteData(outas,outBufS,outBufUsed);
	    break;
	  case AUFILE:
#ifdef USE_AFSP
	    AFwriteData(outaf,outBufF,outBufUsed);
#endif
	    break;
	  case AUDEV:
	    if (PRWriteData(outpr,outBufS,outBufUsed))
	      stop = 1;
	    break;
	  }
	outNumSamp += outBufUsed;

	/* state progression */
	switch (state) {
	case 1:
	  if (stripNumSamp==0) {
	    leadingStripNumSamp = inNumSamp;
	    inStart = inNumSamp;
	    state = 2;
	  }
	  break;
	case 2:
	  if (!inBufEnd)
	    state = 3;
	  break;
	case 3:
	  if (!outBufUsed)
	    state = 4;
	  break;
	case 4:
	  if (!transBufEnd && !stripNumSamp) {
	    if (!durNumSamp)
	      durNumSamp = inNumSamp-inStart-startNumSamp;
	    outEnd = outStart+actOutNumChan*
	      (long)(durNumSamp/inNumChan/inFSample*actOutFSample+.5);
	    state = 5;
	  }
	  break;
	case 5:
	  if (!outBufUsed)
	    state = 6;
	  break;
	}
	if (stop)
	  state = 6;

      } while (state!=6);

      if (rc)
	RateConvFree(rc);

      if (highPassFreq>0) {
	free(hpfState[0]);
	free(hpfState);
      }
      free(bufF);
      free(tmpBufF);
      free(bufS);

    }  /* if (!noAudio) */
      
    /* close input */
    switch (intype) {
    case AUSTREAM:
      AuClose(inas);
      break;
    case AUFILE:
#ifdef USE_AFSP
      AFclose(inaf);
#endif
      break;
    case AUDEV:
      PRClose(inpr);
      break;
    }

    if (!(noAudio || wplaxList)) {

      /* close output */
      switch (outtype) {
      case AUSTREAM:
	AuClose(outas);
	break;
      case AUFILE:
#ifdef USE_AFSP
	AFclose(outaf);
#endif
	break;
      case AUDEV:
	PRClose(outpr);
	break;
      }

      if (debugLevel && debugLevel<=2) {
	fprintf(stderr," \n");
	fflush(stderr);
      }
      if (debugLevel>1) {
	fprintf(stderr,
		"in: %ld samples (%.3f sec)   "
		"out: %ld samples (%.3f sec)\n",
		inNumSamp,inNumSamp/(inNumChan*inFSample),
		outNumSamp,outNumSamp/(actOutNumChan*actOutFSample));
	if (stripSilence)
	  fprintf(stderr,
		  "detected silence:\n"
		  "leading: %ld samples (%.3f sec)  "
		  "trailing: %ld samples (%.3f sec)\n",
		  leadingStripNumSamp,
		  leadingStripNumSamp/(inNumChan*inFSample),
		  trailingStripNumSamp,
		  trailingStripNumSamp/(inNumChan*inFSample));
      }
      if (totClip)
	CommonWarning("%ld samples clipped!",totClip);

    }  /* if (!noAudio) */

    if (outFileNameUsed)
      outFileNameUsed = 0;
    if (varArgIdx[listIdx]>=0)
      listIdx++;
  }

  CmdLineEvalFree(paraList);

  if (debugLevel)
    fprintf(stderr,"%s: %s%s\n",progName,
	    stop?"aborted":"finished",(stop==2)?" (broken pipe)":"");

  exit (0);
}

/* end of plax.c */

