/**********************************************************************
ac3spdif -- encapsulates AC3 in S/P-DIF according to IEC 61937

$Id: ac3spdif.c,v 1.9 2003/01/19 23:17:09 purnhage Exp $

Source file: ac3spdif.c

(c) Heiko Purnhagen 2002
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:
10-Apr-2002 HP      born
11-Apr-2002 HP      and running ...


References:
- Advanced Television Systems Committee Doc. A/52
  "Digital Audio Compression Standard (AC-3)"
  available: http://www.atsc.org/standards/a_52a.pdf
- IEC 61937
  "Digital audio - interface for non-linear PCM encoded audio
  bitstreams applying IEC 60958"
  Part 1 - general; Part 2 - burst info; Part 3 - AC3

Note: This implementation assumes that AC3 files are byte-aligned.

**********************************************************************/


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

#include "common.h"
#include "cmdline.h"
#include "audio.h"

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

#define PROGVER "ac3spdif V1.0 11-Apr-2002"
#define CVSID "$Id: ac3spdif.c,v 1.9 2003/01/19 23:17:09 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
#define BUFLEN 3072	/* AC3 burst periode 1536*2ch */

#define RAWENV "RAWAUDIOFILE"
#define RAWDEFAULT "integer16,0,48000.,big-endian,2,1.0"


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


static char *rawFormat;
static int rawFormatUsed;
static int debugLevel;
static int audioDebugLevel;
static int *varArgIdx;
static int reverse;

static CmdLinePara paraList[] = {
  {&varArgIdx,NULL,"<input file (- = stdin)> <output file (- = stdout)>"},
  {NULL,NULL,NULL}
};

static CmdLineSwitch switchList[] = {
  {"h",NULL,NULL,NULL,NULL,"print help"},
  {"r",&reverse,NULL,NULL,NULL,
   "reverse operation (spdif -> ac3)"},
  {"f",&rawFormat,"%s",NULL,&rawFormatUsed,
   "raw input file format\n"
   "(dflt: $" RAWENV " / " RAWDEFAULT ")"},
  {"d",&debugLevel,"%d","0",NULL,"debug level (0=off 1=basic .. 4=full)"},
  {"da",&audioDebugLevel,"%i","0",NULL,"audio file debug level"},
  {NULL,NULL,NULL,NULL,NULL,NULL}
};


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

#define ac3syncword 0x0b77
#define ac3syncbyte1 0x0b
#define ac3syncbyte2 0x77
static int ac3fscod[4] = {48000, 44100, 32000, 0};
static int ac3frmsizecod[32] = {
  32, 40, 48, 56, 64, 80, 96, 112,
  128, 160, 192, 224, 256, 320, 384, 448,
  512, 576, 640, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0
};
static char *ac3bsmod[8] = {
  "main audio service: complete main (CM)",
  "main audio service: music and effects (ME)",
  "associated service: visually impaired (VI)",
  "associated service: hearing  impaired (HE)",
  "associated service: dialogue (D)",
  "associated service: commentary (C)",
  "associated service: emergency (E)",
  "if 1/0: associated service: voice over (VO)"
  "else: main audio service: karaoke"
};
static char *ac3acmod[8] = {
  "1+1 Ch1,Ch2",
  "1/0 C",
  "2/0 L,R",
  "3/0 L,C,R",
  "2/1 L,R,S",
  "3/1 L,C,R,S",
  "2/2 L,R,SL,SR",
  "3/2 L,C,R,SL,SR"
};
static char *ac3cmixlev[5] = {
  "0.707 (-3.0 dB)",
  "0.596 (-4.5 dB)",
  "0.500 (-6.0 dB)",
  "reserved",
  "not available"
};
static char *ac3surmixlev[5] = {
  "0.707 (-3 dB)",
  "0.500 (-6 dB)",
  "0 (-inf dB)",
  "reserved",
  "not available"
};
static char *ac3dsurmod[5] = {
  "not indicated",
  "NOT Dolby Surround encoded",
  "Dolby Surround encoded",
  "reserved",
  "not available"
};

/* returns number of 16 bit words per syncframe*/
int ac3wordframe (int fscod, int frmsizecod)
{
  switch (fscod) {
  case 0: /* 48000 */
    return 2*ac3frmsizecod[frmsizecod/2];
  case 1: /* 44100 */
    return (int)(96000./44100.*ac3frmsizecod[frmsizecod/2]) + frmsizecod%1;
  case 2: /* 32000 */
    return 3*ac3frmsizecod[frmsizecod/2];
  default:
    return 0;
  }
}

int getbit (unsigned char **c, int *b, int n)
{
  int t = 0;
  int r;
  while (n) {
    r = n;
    if (r > 8-*b)
      r = 8-*b;
    t = (t<<r) | ((**c)>>(8-*b-r)) & ((1<<r)-1);
    n -= r;
    *b += r;
    if (*b==8) {
      *b = 0;
      (*c)++;
    }
  }
  return t;
}

/* returns number of data bytes (0=not_found -1=eof -2=error) */
int getAC3frame (FILE *ac3File, unsigned char *cbuf,
		 int buflen, int *bufleft,
		 int *ac3fSample, long *ac3Bytes)
{
  int got;
  int i;
  int syncword,crc1,fscod,frmsizecod;
  int b;
  int bytes;
  unsigned char *c;

  if (*bufleft)
    for (i=0; i<*bufleft; i++)
      cbuf[i] = cbuf[i+buflen-*bufleft];
  got = fread(cbuf+*bufleft,1,buflen-*bufleft,ac3File);
  *ac3Bytes += got;
  *bufleft += got;
  if (!got && ferror(ac3File))
    return -2;
  if (!got && *bufleft==0)
    return -1; 	/* eof */

  if (*bufleft>=8 && cbuf[0]==ac3syncbyte1 && cbuf[1]==ac3syncbyte2) {
    b = 0;
    c = cbuf;
    syncword = getbit(&c,&b,16);
    crc1 = getbit(&c,&b,16);
    fscod = getbit(&c,&b,2);
    frmsizecod = getbit(&c,&b,6);
    *ac3fSample = ac3fscod[fscod];
    bytes = ac3wordframe(fscod,frmsizecod)*2;
    if (bytes>*bufleft) {
      CommonWarning("getAC3frame: frame incomplete or too long (%d)",bytes);
      bytes = *bufleft;
    }
    if (bytes<8) {
      CommonWarning("getAC3frame: frame too short (%d)",bytes);
      bytes = 8;
    }
    *bufleft -= bytes;
    return bytes;
  }

  i = 1;
  while (i<*bufleft && cbuf[i] != ac3syncbyte1)
    i++;
  *bufleft -= i;
  return 0;
}

void showAC3info (unsigned char *cbuf, int mode, FILE *stream)
{
  int syncword;
  int crc1,fscod,frmsizecod,bsid;
  int bsmod,acmod,cmixlev,surmixlev,dsurmod,lfeon,dialnorm;
  static int fscodLast,frmsizecodLast,bsidLast,bsmodLast,acmodLast,lfeonLast;
  int b;
  unsigned char *c;

  b = 0;
  c = cbuf;
  syncword = getbit(&c,&b,16);
  crc1 = getbit(&c,&b,16);
  fscod = getbit(&c,&b,2);
  frmsizecod = getbit(&c,&b,6);
  bsid = getbit(&c,&b,5);
  bsmod = getbit(&c,&b,3);
  acmod = getbit(&c,&b,3);
  if ((acmod & 0x1) && (acmod != 0x1))
    cmixlev = getbit(&c,&b,2);
  else
    cmixlev = 4;
  if (acmod & 0x4)
    surmixlev = getbit(&c,&b,2);
  else
    surmixlev = 4;
  if (acmod == 0x2)
    dsurmod = getbit(&c,&b,2);
  else
    dsurmod = 4;
  lfeon = getbit(&c,&b,1);
  dialnorm = getbit(&c,&b,5);

  if (mode==0)
    fscodLast = frmsizecodLast = bsidLast = bsmodLast =
      acmodLast = lfeonLast = -1;
  
  if (fscodLast != fscod ||
      (frmsizecodLast | 1) != (frmsizecod | 1) ||
      bsidLast != bsid ||
      bsmodLast != bsmod ||
      acmodLast != acmod ||
      lfeonLast != lfeon) {
    if (mode==2)
      fprintf(stream,"  \n");
    fprintf(stream,"***** AC3 bit stream information *****\n");
    fprintf(stream,"  syncword (0x%04x):        0x%04x \n",
	    syncword,ac3syncword);
    fprintf(stream,"  crc1:                     0x%04x\n",
	    crc1);
    fprintf(stream,"  sample rate code:         %d Hz  [0x%02x]\n",
	    ac3fscod[fscod],fscod);
    fprintf(stream,"  frame size code:          %d kbps  [0x%02x]\n",
	    ac3frmsizecod[frmsizecod/2],frmsizecod);
    fprintf(stream,"  -> 16bit-words/syncframe: %d\n",
	    ac3wordframe(fscod,frmsizecod));
    fprintf(stream,"  bit stream id (8):        %d  [0x%02x]\n",
	    bsid,bsid);
    fprintf(stream,"  bit stream mode:          %s  [0x%02x]\n",
	    ac3bsmod[bsmod],bsmod);
    fprintf(stream,"  audio coding mode:        %s  [0x%02x]\n",
	    ac3acmod[acmod],acmod);
    fprintf(stream,"  center mix level:         %s  [0x%02x]\n",
	    ac3cmixlev[cmixlev],0xff & -1+(cmixlev+1)%5);
    fprintf(stream,"  surround mix level:       %s  [0x%02x]\n",
	    ac3surmixlev[surmixlev],0xff & -1+(surmixlev+1)%5);
    fprintf(stream,"  Dolby Surround mode:      %s  [0x%02x]\n",
	    ac3dsurmod[dsurmod],0xff & -1+(dsurmod+1)%5);
    fprintf(stream,"  lfe channel on:           %s  [0x%02x]\n",
	    lfeon?"yes":"no",lfeon);
    fprintf(stream,"  dialogue normalization:   -%d dB  [0x%02x]\n",
	    dialnorm?dialnorm:31,dialnorm);
    if (mode==2)
      fprintf(stream,"\n");
    fscodLast = fscod;
    frmsizecodLast = frmsizecod;
    bsidLast = bsid;
    bsmodLast = bsmod;
    acmodLast = acmod;
    lfeonLast = lfeon;
  }
}

void buildSPDIFframe (unsigned char *cbuf, int result,
		      short *sbuf, float *fbuf, int buflen)
{	
  int i;

  if ((result+1)/2+4>buflen) {
    CommonWarning("buildSPDIFframe: frame too long");
    result = (buflen-4)*2;
  }
  sbuf[0] = (short)0xf872;	/* Pa  sync word 1 */
  sbuf[1] = 0x4e1f;		/* Pb  sync word 2 */
  sbuf[2] = 0x0001;		/* Pc  burst-info (data-type = AC3) */
  sbuf[3] = result*8;		/* Pd  length-code (bits) */
  if (result%2)
    cbuf[result] = 0;	/* add padding byte */
  for (i=0; i<(result+1)/2; i++)
    sbuf[i+4] = (cbuf[2*i]<<8) | (cbuf[2*i+1] & 0xff);
  for (i+=4; i<BUFLEN; i++)
    sbuf[i] = 0;
  for (i=0; i<BUFLEN; i++)
    fbuf[i] = sbuf[i];
}

/* returns number of data bits (0=not_found -1=eof -2=error) */
int getSPDIFframe (AudioFile *auFile, float *fbuf, short *sbuf,
		   int buflen, int *bufleft,
		   unsigned char *cbuf, int *dataType, long *spdifBytes)
{
  int got;
  int i,t;
  int bits;
  int burstInfo;

  if (*bufleft)
    for (i=0; i<*bufleft; i++)
      sbuf[i] = sbuf[i+buflen-*bufleft];
  got = AudioReadDataIntlvd(auFile,fbuf,buflen-*bufleft);
  *spdifBytes += 2*got;
  for (i=0; i<got; i++)
    sbuf[*bufleft+i] = (short)((long)(fbuf[i]+32768.5)-32768);
  *bufleft += got;
  if (!got && *bufleft==0)
    return -1; 	/* eof */

  if (*bufleft>=8 && sbuf[0]==(short)0xf872 && sbuf[1]==0x4e1f) {
    burstInfo = sbuf[2];
    bits = (unsigned short)sbuf[3];
    *dataType = burstInfo & 0x001f;
    t = (bits+15)/16;
    if (t+4>*bufleft) {
      CommonWarning("getSPDIFframe: frame incomplete or too long");
      t = *bufleft-4;
    }
    for (i=0; i<t; i++) {
      cbuf[2*i] = sbuf[i+4]>>8;
      cbuf[2*i+1] = sbuf[i+4] & 0xff;
    }
    *bufleft -= t+4;
    return bits;
  }

  i = 1;
  while (i<*bufleft && sbuf[i] != (short)0xf872)
    i++;
  *bufleft -= i;
  return 0;
}

/* returns 0 if wrong AC3 syncword */
int checkAC3frame (unsigned char *cbuf, int *ac3fSample)
{
  int syncword,crc1,fscod,frmsizecod;
  int b;
  unsigned char *c;

  b = 0;
  c = cbuf;
  syncword = getbit(&c,&b,16);
  crc1 = getbit(&c,&b,16);
  fscod = getbit(&c,&b,2);
  frmsizecod = getbit(&c,&b,6);
  *ac3fSample = ac3fscod[fscod];
  return (syncword == ac3syncword)?1:0;
}


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

int main (int argc, char *argv[])
{
  char *progName = "<no program name>";
  int result;
  AudioFile *auFile;
  FILE *ac3File;
  float fbuf[BUFLEN];
  short sbuf[BUFLEN];
  unsigned char cbuf[2*BUFLEN];
  int numChan;
  float fSample;
  long numSample;
  long inNumByte,outNumByte,frames;
  long ac3Bytes,spdifBytes;
  int bufleft;
  int ac3fSample,ac3fSampleLast;
  int dataType;
  int isPause;

  /* 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",
	      progName,PROGVER "   " DISTVER,CVSID);
      CmdLineHelp(progName,paraList,switchList,stderr);
      fprintf(stderr,
              "\n"
              "This tools implements the encapsulation of AC3 bit streams\n"
	      "for transmission over IEC 60958 (S/P-DIF digital audio interface)\n"
	      "according to IEC 61937. The audio file format is auto-detected\n"
	      "from header or extension (stdin/stdout support .au only).\n"
	      "Example: ac3spdif demo.ac3 - | plax -\n"
              "\n");
      exit (1);
    }
    else
      CommonExit(1,"command line error (\"-h\" for help)");
  }

  if (debugLevel)
    fprintf(stderr,"%s: %s\n",progName,PROGVER "   " DISTVER);
  if (debugLevel>1)
    fprintf(stderr,"%s\n",CVSID);
  
  if (!rawFormatUsed)
    if ((rawFormat=getenv(RAWENV))==NULL)
      rawFormat = RAWDEFAULT;
  AudioInit(rawFormat,0,audioDebugLevel);

  if (varArgIdx[0]<0 || varArgIdx[1]<0 || varArgIdx[2]>=0)
    CommonExit(1,"input and outout file required");

  inNumByte = outNumByte = frames = 0;
  ac3Bytes = spdifBytes = 0;
  bufleft = 0;
  fSample = 1.;
  isPause = 0;

  if (!reverse) {

    /* AC3 -> S/P-DIF */
    if (debugLevel)
      fprintf(stderr,
	      "%s: AC3 %s -> S/P-DIF %s\n",
	      progName,argv[varArgIdx[0]],argv[varArgIdx[1]]);

    if (!strcmp(argv[varArgIdx[0]],"-"))
      ac3File = stdin;
    else
      if (!(ac3File=fopen(argv[varArgIdx[0]],"rb")))
	CommonExit(1,"error opening AC3 input file %s",argv[varArgIdx[0]]);

    while (!(result=getAC3frame(ac3File,cbuf,2*BUFLEN,&bufleft,
				&ac3fSample,&ac3Bytes))) {
      if (debugLevel>2) {
	fprintf(stderr,"%c%9.3f %8ld (%4d, %10ld/%10ld -> %10ld/%10ld)",
		(debugLevel>3)?'\n':'\r',spdifBytes/4/fSample,frames,
		result,
		inNumByte,ac3Bytes,outNumByte,spdifBytes);
	fflush(stderr);
      }
    }
    if (result==-2)
      CommonExit(1,"error reading AC3 input file %s",argv[varArgIdx[0]]);
    if (result==-1)
      CommonExit(1,"can not find AC3 frame");
    if (debugLevel>1)
      showAC3info(cbuf,(debugLevel>2)?2:0,stderr);
    ac3fSampleLast = ac3fSample;
    inNumByte += result;
    fSample = ac3fSample;
    numChan = 2;
    if (debugLevel>2)
      fprintf(stderr,"audio file: numChan=%d fSample=%f\n",
	      numChan,fSample);

    if (!(auFile=AudioOpenWrite(argv[varArgIdx[1]],argv[varArgIdx[1]],
				numChan,fSample)))
      CommonExit(1,"error opening output audio file %s",argv[varArgIdx[1]]);

    while (result) {
      buildSPDIFframe(cbuf,result,sbuf,fbuf,BUFLEN);
#if 0
      {
	int j;
	fprintf(stderr,"\nBUFLEN=%d\n",BUFLEN);
	for (j=0; j<16; j++)
	  fprintf(stderr,"0x%02x: 0x%02x 0x%02x 0x%04x %d %.1f\n",
		  j,cbuf[2*j],cbuf[2*j+1],sbuf[j],sbuf[j],fbuf[j]);
	fprintf(stderr,"\n");
      }
#endif
      AudioWriteDataIntlvd(auFile,fbuf,BUFLEN);
      outNumByte += ((result+1)/2+4)*2;
      spdifBytes += BUFLEN*2;
      frames++;

      if (debugLevel>2) {
	fprintf(stderr,"%c%9.3f %8ld (%4d, %10ld/%10ld -> %10ld/%10ld)",
		(debugLevel>3)?'\n':'\r',spdifBytes/4/fSample,frames,
		result,
		inNumByte,ac3Bytes,outNumByte,spdifBytes);
	fflush(stderr);
      }

      while (!(result=getAC3frame(ac3File,cbuf,2*BUFLEN,&bufleft,
				  &ac3fSample,&ac3Bytes))) {
	if (debugLevel>2) {
	  fprintf(stderr,"%c%9.3f %8ld (%4d, %10ld/%10ld -> %10ld/%10ld)",
		  (debugLevel>3)?'\n':'\r',spdifBytes/4/fSample,frames,
		  result,
		  inNumByte,ac3Bytes,outNumByte,spdifBytes);
	  fflush(stderr);
	}
      }
      if (result==-2)
	CommonExit(1,"error reading AC3 input file %s",argv[varArgIdx[0]]);
      if (result==-1)
	result = 0;
      inNumByte += result;
      if (ac3fSampleLast != ac3fSample) {
	if (debugLevel>2)
	  fprintf(stderr,"\n");
	CommonWarning("AC3 sampling rate changed from %d to %d",
		      ac3fSampleLast,ac3fSample);
	ac3fSampleLast = ac3fSample;
      }
      if (result && debugLevel>1)
	showAC3info(cbuf,(debugLevel>2)?2:1,stderr);
    }

    if (strcmp(argv[varArgIdx[0]],"-"))
      if (fclose(ac3File))
	CommonExit(1,"error closing AC3 input file %s",argv[varArgIdx[0]]);
    AudioClose(auFile);

  }
  else {

    /* S/P-DIF -> AC3 */
    if (debugLevel)
      fprintf(stderr,
	      "%s: S/P-DIF %s -> AC3 %s\n",
	      progName,argv[varArgIdx[0]],argv[varArgIdx[1]]);

    if (!(auFile=AudioOpenRead(argv[varArgIdx[0]],
			       &numChan,&fSample,&numSample)))
      CommonExit(1,"error opening input audio file %s",argv[varArgIdx[0]]);
    if (debugLevel>1)
      fprintf(stderr,"audio file: numChan=%d fSample=%f numSample=%ld\n",
	      numChan,fSample,numSample);
    if (numChan != 2)
      CommonExit(1,"expected 2 channels (%d)",numChan);
    if (fSample != 32000 && fSample != 44100 && fSample != 48000)
      CommonExit(1,"expected 32/44.1/48 kHz sampling rate (%f.0)",fSample);


    while (!(result=getSPDIFframe(auFile,fbuf,sbuf,BUFLEN,&bufleft,
			   cbuf,&dataType,&spdifBytes))) {
      if (debugLevel>2) {
	fprintf(stderr,"%c%9.3f %8ld (%4d, %10ld/%10ld -> %10ld/%10ld)",
		(debugLevel>3)?'\n':'\r',spdifBytes/4/fSample,frames,
		(result>=0)?(result+7)/8:result,
		inNumByte,spdifBytes,outNumByte,ac3Bytes);
	fflush(stderr);
      }
    }
    if (result==-2)
      CommonExit(1,"error reading S/P-DIF input file %s",argv[varArgIdx[0]]);
    if (result==-1)
      CommonExit(1,"can not find S/P-DIF frame");

    if (result%8) {
      CommonWarning("playload not byte-aligned (%d)",result);
      result = (result | 0x3)+1;
    }
    inNumByte += 2*4+(result/8);
    if (dataType != 1)
      CommonExit(1,"data-type not AC3 (%d)",dataType);
    if (!checkAC3frame(cbuf,&ac3fSample))
      CommonExit(1,"wrong AC3 syncword");
    if (ac3fSample != fSample)
      CommonWarning("AC3 sampling rate %d (expected %f.0)",ac3fSample,fSample);
    ac3fSampleLast = ac3fSample;
    if (debugLevel>1)
      showAC3info(cbuf,(debugLevel>2)?2:0,stderr);

    if (!strcmp(argv[varArgIdx[1]],"-"))
      ac3File = stdout;
    else
      if (!(ac3File=fopen(argv[varArgIdx[1]],"wb")))
	CommonExit(1,"error opening AC3 output file %s",argv[varArgIdx[1]]);

    while (result) {
      if (result/8 != (int)fwrite(cbuf,1,result/8,ac3File))
	CommonExit(1,"error writing AC3 output file %s",argv[varArgIdx[1]]);
      outNumByte += result/8;
      ac3Bytes += result/8;
      frames++;

      if (debugLevel>2) {
	fprintf(stderr,"%c%9.3f %8ld (%4d, %10ld/%10ld -> %10ld/%10ld)",
		(debugLevel>3)?'\n':'\r',spdifBytes/4/fSample,frames,
		(result>=0)?(result+7)/8:result,
		inNumByte,spdifBytes,outNumByte,ac3Bytes);
	fflush(stderr);
      }

      while (!(result=getSPDIFframe(auFile,fbuf,sbuf,BUFLEN,&bufleft,
				    cbuf,&dataType,&spdifBytes))) {
	if (debugLevel>2) {
	  fprintf(stderr,"%c%9.3f %8ld (%4d, %10ld/%10ld -> %10ld/%10ld)",
		  (debugLevel>3)?'\n':'\r',spdifBytes/4/fSample,frames,
		  (result>=0)?(result+7)/8:result,
		  inNumByte,spdifBytes,outNumByte,ac3Bytes);
	  fflush(stderr);
	}
      }
      if (result==-2)
	CommonExit(1,"error reading S/P-DIF input file %s",argv[varArgIdx[0]]);
      if (result==-1)
	result = 0;
      if (result%8) {
	CommonWarning("playload not byte-aligned (%d)",result);
	result = (result | 0x3)+1;
      }
      inNumByte += 2*4+(result/8);
      if (dataType == 3) {
	if (!isPause) {
	  if (debugLevel>2)
	    fprintf(stderr,"\n");
	  CommonWarning("data-type Pause (%d) [%ld bytes in AC3 file]",
			dataType,ac3Bytes);
	}
	isPause = 1;
	continue;
      }
      if (dataType != 1)
	CommonWarning("data-type not AC3 (%d)",dataType);
      if (!checkAC3frame(cbuf,&ac3fSample))
	CommonWarning("wrong AC3 syncword");
      if (ac3fSampleLast != ac3fSample) {
	if (debugLevel>2)
	  fprintf(stderr,"\n");
	CommonWarning("AC3 sampling rate changed from %d to %d",
		      ac3fSampleLast,ac3fSample);
	ac3fSampleLast = ac3fSample;
      }
      if (result && debugLevel>1)
	showAC3info(cbuf,(debugLevel>2)?2:(isPause?0:1),stderr);
      isPause = 0;
    }

    AudioClose(auFile);
    if (strcmp(argv[varArgIdx[1]],"-"))
      if (fclose(ac3File))
	CommonExit(1,"error closing AC3 output file %s",argv[varArgIdx[1]]);

  }

  CmdLineEvalFree(paraList);

  if (debugLevel>2)
    fprintf(stderr,"\n");
  if (debugLevel)
    fprintf(stderr,"%s: %.3f s (%ld frames, %ld/%ld -> %ld/%ld bytes)\n",
	    progName,spdifBytes/4/fSample,frames,
	    inNumByte,reverse?spdifBytes:ac3Bytes,
	    outNumByte,reverse?ac3Bytes:spdifBytes);

  return 0;
}

/* end of ac3spdif.c */
