/**********************************************************************
 * ISO MPEG Audio Subgroup Software Simulation Group (1996)
 * ISO 13818-3 MPEG-2 Audio Encoder - Lower Sampling Frequency Extension
 *
 * $Id: musicin.c,v 1.2 1997/01/19 22:28:29 rowlands Exp $
 *
 * $Log: musicin.c,v $
 * Revision 1.2  1997/01/19 22:28:29  rowlands
 * Layer 3 bug fixes from Seymour Shlien
 *
 * Revision 1.1  1996/02/14 04:04:23  rowlands
 * Initial revision
 *
 * Received from Mike Coleman
 **********************************************************************/
#include <fcntl.h>


#include <stdlib.h>
#include "common.h"
#include "encoder.h"
#include "l3psy.h"
#include "mdct.h"
#include "loop.h"
#include "l3bitstream.h"
#include "gtkanal.h"
#include <assert.h>

#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
struct stat sb;
long totalframes;

int autoconvert=FALSE;
int force_ms=FALSE;
int firstcall = 1;

/* Global variable definitions for "musicin.c" */

int swapbytes = FALSE; /* force byte swapping */
FILE               *musicin;
Bit_stream_struc   bs;
char               *programName;
int iswav=0;





short FAR BigBuf[2][2304];

FLOAT snr32[32];
L3SBS  FAR     l3_sb_sample;

III_side_info_t l3_side;
frame_params fr_ps;
unsigned long frameBits, sentBits = 0;
unsigned long bitsPerSlot;
double frac_SpF;
int whole_SpF;
unsigned long num_samples;
int frameNum=0;
#ifdef DISTRIB
int gtkflag=1;
#else
int gtkflag=0;
#endif
int gpsycho=1;
int sfb21=1;
#ifdef _BLADEDLL
int silent=1;
#else
int silent=0;
#endif
int highq=0;

static int MPGLAG=1;
static int fast=0;
int original_file_type;  /* 0=WAV, 1=MP3 */
int original_file_fd;    /* file descriptor */

/* Implementations */

/*********************************************************/
/* Timeconvert: convert given time (in secs) to hh:mm:ss */
/*********************************************************/
void timestatus(int frameNum, int totalframes, int samp_rate)
{   
  int time_so_far;
  int remain,estimated;
  double speedup;
  char str1[20],str2[20],str3[20];

  time_so_far = clock()/CLOCKS_PER_SEC;
  /* calc estimated total CPU time */
  estimated = (int)((float)totalframes/(float)frameNum * (float)time_so_far);
  remain=estimated - time_so_far;

  /* estimated encoding time / playing time.  (protected from divide by 0) */
  speedup =   (estimated*totalframes*1.152)/
    (1e-12+samp_rate*estimated*estimated);  
  
  sprintf(str1,"%02d:%02d",(time_so_far/60)%60, time_so_far%60);
  sprintf(str2,"%02d:%02d",(remain/60)%60, remain%60);
  sprintf(str3,"%02d:%02d",(estimated/60)%60, estimated%60);

  fprintf(stderr, 
"Frame: [%4lu/%4lu]   Encoding Time %5s/%5s  (%.2fx)   ETA %5s\r", 
frameNum,totalframes,str1,str3,speedup,str2);
    fflush(stderr);
}




/************************************************************************
*
* parse_args
*
* PURPOSE:  Sets encoding parameters to the specifications of the
* command line.  Default settings are used for parameters
* not specified in the command line.
*
* If the input file is in AIFF format, the sampling frequency is read
* from the AIFF header.
*
* The input and output filenames are read into #inpath# and #outpath#.
*
************************************************************************/
 
void
parse_args(argc, argv, fr_ps, psy, num_samples, inPath, outPath)
int     argc;
char    **argv;
frame_params  *fr_ps;
int     *psy;
unsigned long *num_samples;
char    inPath[MAX_NAME_SIZE];
char    outPath[MAX_NAME_SIZE];
{
   FLOAT srate;
   int   brate;
   layer *info = fr_ps->header;
   int   err = 0, i = 0;
   IFF_AIFF pcm_aiff_data;
   long samplerate;
   long soundPosition;
   char ckwav[5]={0,0,0,0,0};
 
   /* preset defaults */
   inPath[0] = '\0';   outPath[0] = '\0';
   info->lay = DFLT_LAY;
   switch(DFLT_MOD) {
      case 's': info->mode = MPG_MD_STEREO; info->mode_ext = 0; break;
      case 'd': info->mode = MPG_MD_DUAL_CHANNEL; info->mode_ext=0; break;
      case 'j': info->mode = MPG_MD_JOINT_STEREO; info->mode_ext=0; break;
      case 'm': info->mode = MPG_MD_MONO; info->mode_ext = 0; break;
      default:
         fprintf(stderr, "%s: Bad mode dflt %c\n", programName, DFLT_MOD);
         abort();
   }
   *psy = DFLT_PSY;
   if((info->sampling_frequency = SmpFrqIndex((long)(1000*DFLT_SFQ), &info->version)) < 0) {
      fprintf(stderr, "%s: bad sfrq default %.2f\n", programName, DFLT_SFQ);
      abort();
   }
  info->bitrate_index = 9;
  brate = 0;
   switch(DFLT_EMP) {
      case 'n': info->emphasis = 0; break;
      case '5': info->emphasis = 1; break;
      case 'c': info->emphasis = 3; break;
      default: 
         fprintf(stderr, "%s: Bad emph dflt %c\n", programName, DFLT_EMP);
         abort();
   }
   info->copyright = 0;
   info->original = 1;
   info->error_protection = FALSE;
 
   /* process args */
   while(++i<argc && err == 0) {
      char c, *token, *arg, *nextArg;
      int  argUsed;
 
      token = argv[i];
      if(*token++ == '-') {
         if(i+1 < argc) nextArg = argv[i+1];
         else           nextArg = "";
         argUsed = 0;
	 if (! *token) {
	   /* The user wants to use stdin and/or stdout. */
	   if(inPath[0] == '\0')       strcpy(inPath, argv[i]);
	   else if(outPath[0] == '\0') strcpy(outPath, argv[i]);
	 } 
         while( (c = *token++) ) {
            if(*token ) arg = token;
            else                             arg = nextArg;
            switch(c) {
               case 'm':        argUsed = 1;
                  if (*arg == 's')
                    { info->mode = MPG_MD_STEREO; info->mode_ext = 0; }
                  else if (*arg == 'd')
                    { info->mode = MPG_MD_DUAL_CHANNEL; info->mode_ext=0; }
                  else if (*arg == 'j')
                    { info->mode = MPG_MD_JOINT_STEREO; info->mode_ext=0; }
                  else if (*arg == 'f')
                    { info->mode = MPG_MD_JOINT_STEREO; force_ms=1; info->mode_ext=0; }
                  else if (*arg == 'm')
                    { info->mode = MPG_MD_MONO; info->mode_ext = 0; }
                  else {
                    fprintf(stderr,"%s: -m mode must be s/d/j/f/m not %s\n",
                            programName, arg);
                    err = 1;
                  }
                  break;

               case 's':
                  argUsed = 1;
                  srate = atof( arg );
                  /* samplerate = rint( 1000.0 * srate ); $A  */
                  samplerate = (long) (( 1000.0 * srate ) + 0.5);
                  if( (info->sampling_frequency =
          SmpFrqIndex((long) samplerate, &info->version)) < 0 )
                      err = 1;
                  break;
                  
	    case 'b':        
		 argUsed = 1;
		 brate = atoi(arg); 
		 break;
            case 'x':  /* force byte swapping */
	      swapbytes=TRUE;
	      break;
	    case 'a': /* autoconvert input file from stereo to mono - for mono mp3 encoding */
	      autoconvert = TRUE;
	      break;
	    case 'h': 
	      highq = TRUE;
	      break;
	    case 'k': 
	      sfb21 = FALSE;
	      break;
	    case 'S': 
	      silent = TRUE;
	      break;
	    case 'f': 
	      fast = TRUE;
	      break;
	    case 'O': 
	      gpsycho = FALSE;
	      break;
#ifdef HAVEGTK
	    case 'g': /* turn on gtk analysis */
	      gtkflag = TRUE;
#endif
	      break;
	    case 'e':        argUsed = 1;
                  if (*arg == 'n')                    info->emphasis = 0;
                  else if (*arg == '5')               info->emphasis = 1;
                  else if (*arg == 'c')               info->emphasis = 3;
                  else {
                     fprintf(stderr,"%s: -e emp must be n/5/c not %s\n",
                             programName, arg);
                     err = 1;
                  }
                  break;
                case 'c':       info->copyright = 1; break;
                case 'o':       info->original  = 0; break;
                default:        fprintf(stderr,"%s: unrec option %c\n",
                                        programName, c);
                                err = 1; break;
            }
            if(argUsed) {
               if(arg == token)    token = "";   /* no more from token */
               else                ++i;          /* skip arg we used */
               arg = ""; argUsed = 0;
            }
         }
      }
      else {
         if(inPath[0] == '\0')       strcpy(inPath, argv[i]);
         else if(outPath[0] == '\0') strcpy(outPath, argv[i]);
         else {
            fprintf(stderr,"%s: excess arg %s\n", programName, argv[i]);
            err = 1;
         }
      }
   }

   if(err || inPath[0] == '\0') usage();  /* never returns */
 
   if (inPath[0]=='-') silent=1;  /* turn off status - it's broken for stdin */


   if(outPath[0] == '\0') {
     if (inPath[0]=='-') {
       /* if input is stdin, default output is stdout */
       strcpy(outPath,"-");
     }else {
       strcpy(outPath, inPath);
       strcat(outPath, DFLT_EXT);
     }
     
   }

// SPECIAL FOR BLADE ENCODER
#ifdef _BLADEDLL
     open_bit_stream_w(&bs, outPath, BUFFER_SIZE);
#else
   original_file_type=!(strcmp((char *) &inPath[strlen(inPath)-4],".mp3"));

   if (original_file_type) {
     /* MP3 file.  */
     totalframes = -1;
     original_file_fd = open(inPath,O_RDONLY);
     if (original_file_fd == -1) {
       fprintf(stderr,"Could not find \"%s\".\n", inPath);
       exit(1);
     }
     if (!gtkflag) {
       fprintf(stderr,"Use option -g to analyze mp3 files.\n");
       exit(1);
     }
     
   }
   else {
     if (!strcmp(inPath, "-")) {
       /* Read from standard input. */
       musicin = stdin;
     } else {
       if ((musicin = fopen(inPath, "rb")) == NULL) {
	 fprintf(stderr, "Could not find \"%s\".\n", inPath);
	 exit(1);
       }
     }
#ifdef DISTRIB
     outPath="/dev/null";
#endif
     open_bit_stream_w(&bs, outPath, BUFFER_SIZE);
     if ((soundPosition = aiff_read_headers(musicin, &pcm_aiff_data)) != -1) {
       
       fprintf(stderr, "Found Audio IFF sound file headers: ");
       
       aiff_check(inPath, &pcm_aiff_data, &info->version);
       
       if (fseek(musicin, soundPosition, SEEK_SET) != 0) {
	 /*
         fprintf(stderr, "Could not seek to PCM sound data in \"%s\".\n", inPath);
         exit(1);
	 */
	 fprintf(stderr,"Could not seek to PCM sound data in \"%s\".\n",inPath);
	 fprintf(stderr,"Crossing Fingers.....\n");

       }
       
       info->sampling_frequency = SmpFrqIndex((long)pcm_aiff_data.sampleRate, &info->version);
       fprintf(stderr, "%.1f kHz samples\n",
	       pcm_aiff_data.sampleRate/1000);
       
       /* Determine number of samples in sound file */
       
       *num_samples = (long)(pcm_aiff_data.numChannels) *
	 (long)(pcm_aiff_data.numSampleFrames);
       
       if ( pcm_aiff_data.numChannels == 1 ) {
	 info->mode = MPG_MD_MONO;
	 info->mode_ext = 0;
       }
     }
     else {    /* Not using Audio IFF sound file headers. */
       
       if (fseek(musicin, 8, SEEK_SET) != 0) {
	 /*
         fprintf(stderr, "Could not seek to PCM sound data in \"%s\".\n", inPath);
         exit(1);
	 */
	 fprintf(stderr,"Could not seek to PCM sound data in \"%s\".\n",inPath);
	 fprintf(stderr,"Crossing Fingers.....\n");
       }
       
       fread(ckwav, 4, 1, musicin);
       if (strncmp(ckwav, "WAVE",4)==0) {
	 iswav = 1;
	 fseek(musicin, 0x2c, SEEK_SET); /* skip wav file header */
	 /* NO PARSING OF WAV HEADERS YET */
	 fprintf(stderr, "%s : Found WAV header. Assuming 44.1khz 16bit stereo samples\n",ckwav);
       }
       else {
	 fseek(musicin, 0, SEEK_SET);
	 fprintf(stderr, "Assuming raw pcm input file");
	 if (swapbytes==TRUE)
	   fprintf(stderr, " : Forcing byte-swapping\n");
	 else
	   fprintf(stderr, "\n");
       }
       
       /* Declare sound file to have "infinite" number of samples. */
       *num_samples = MAX_U_32_NUM;
     }
     /* Timing, autoconvert and resampling checks and setup */
     stat(inPath,&sb);
     totalframes = sb.st_size/4608;
     if ((info->mode == MPG_MD_MONO)&&(autoconvert==FALSE))
       totalframes *= 2;
     if (autoconvert==TRUE) {
       fprintf(stderr, "Autoconverting from stereo to mono. Setting encoding to mono mode.\n");
       info->mode = MPG_MD_MONO; 
       info->mode_ext = 0;
     }
   }
#endif // END OF BLADE ENCODER SPECIAL
   
   if ( brate == 0 )
     brate = bitrate[info->version][info->lay-1][9];
   if( (info->bitrate_index = BitrateIndex(info->lay, brate, info->version)) < 0)
	   err=1;
   if(err || inPath[0] == '\0') usage();  /* never returns */
   
}

/************************************************************************
 *
 * print_config
 *
 * PURPOSE:  Prints the encoding parameters used
 *
 ************************************************************************/

void print_config( frame_params *fr_ps, int *psy, char *inPath, char *outPath)
{
  layer *info = fr_ps->header;
#if 0 
  fprintf(stderr, "Encoding configuration:\n");
  fprintf(stderr, "Algorithm=%s\n", version_names[info->version]);
  if(info->mode != MPG_MD_JOINT_STEREO)
    fprintf(stderr, "Layer=III   mode=%s   extn=%d   psy model=%d\n",
	    mode_names[info->mode], info->mode_ext, *psy);
  else fprintf(stderr, "Layer=III   mode=%s   extn=data dependant   psy model=%d\n",
	       mode_names[info->mode], *psy);
  fprintf(stderr, "samp frq=%.1f kHz   total bitrate=%d kbps\n",
	  s_freq[info->version][info->sampling_frequency],
	  bitrate[info->version][info->lay-1][info->bitrate_index]);
  fprintf(stderr, "de-emph=%d   c/right=%d   orig=%d\n",
	  info->emphasis, info->copyright, info->original);
  fprintf(stderr, "input file: '%s'   output file: '%s'\n", inPath, outPath);
#endif
  if (original_file_type) {
    fprintf(stderr, "Analyzing %s \n",inPath);
  }
  else {
    fprintf(stderr, "Encoding %s to %s\n",
	    (strcmp(inPath, "-")? inPath : "stdin"),
	    (strcmp(outPath, "-")? outPath : "stdout"));
    fprintf(stderr, "Encoding as %.1f kHz %d kbps %s MPEG-1 LayerIII file\n",
	    s_freq[info->version][info->sampling_frequency],
	    bitrate[info->version][info->lay-1][info->bitrate_index],
	    mode_names[info->mode]);
  }
}


#ifdef HAVEGTK
int readframe()
{
  int i,j,ch,stereo,out=0;
  short mpg123pcm[2][1152];
  static int frameNum=0;

  stereo = fr_ps.stereo;
  frameNum++;

  /* create a 1 frame lag so MP3 analysis syncs with WAV analysis */
  if (frameNum == 1 ) out=1152;
  else out=decode1file(original_file_fd,mpg123pcm,stereo);


  /* delay pcm data by yet another frame */
  for ( ch = 0; ch < stereo; ch++ ) {
    for ( j = 0; j < DECDELAY; j++ )
      pinfo->pcmdata2[ch][j] = pinfo->pcmdata2[ch][j+1152];
    for ( j = 0; j < 1152; j++ ) {
      pinfo->pcmdata2[ch][j+DECDELAY] = mpg123pcm[ch][j];
    }
  }
  MPGLAG = 2;
  pinfo->frameNum = frameNum;
  pinfo->mpglag = MPGLAG;
  return out;
}
#endif


int makeframe()
{
  int j,ch,gr,sideinfo_len,mean_bits;
  double xr[2][2][576];
  double xr_dec[2][2][576];
  double pe[2][2];
  double pe3[2];
  int l3_enc[2][2][576];
  III_psy_ratio ratio,ratio3;
  III_scalefac_t scalefac;
  int blocktype[2];
  int blocktype3[2];
  int mode_gr, bitsPerFrame;
  short *win_buf[2];
  double win_que[2][HAN_SIZE];
  int stereo;
  int iread;
  layer *info;
  unsigned long samplesPerFrame;
  double avg_slots_per_frame;
  static double slot_lag;
  int i;
  char mpg123bs[BUFFER_SIZE];
  short mpg123pcm[2][1152];
  static int mpg123count=0;
  int check_ms_stereo;
  int bit_rate;
  double ms_ener_ratio[2];

  stereo = fr_ps.stereo;
  info = fr_ps.header;
#ifdef HAVEGTK
  if (gtkflag) pinfo->frameNum = frameNum;
#endif

  info->mode_ext = 0; 
  /* use force m/s stereo? */
  if (force_ms) info->mode_ext = 10;

  /* use m/s stereo? */
  check_ms_stereo =   ((info->mode == MPG_MD_JOINT_STEREO) && 
		       (!force_ms) &&
		       (stereo==2) &&
		       (gpsycho));
  

  bit_rate = bitrate[info->version][info->lay-1][info->bitrate_index];
  
  if (firstcall)  {    
    firstcall = 0;
	mpg123count=0;

    /* Figure average number of 'slots' per frame. */
    /* Bitrate means TOTAL for both channels, not per side. */
    bitsPerSlot = 8;
    samplesPerFrame = info->version == 1 ? 1152 : 576;
    avg_slots_per_frame = ((double)samplesPerFrame /
			   s_freq[info->version][info->sampling_frequency]) *
      ((double)bit_rate /  (double)bitsPerSlot);
    whole_SpF = (int) avg_slots_per_frame;
    /* fprintf(stderr, "slots/frame = %d\n",whole_SpF); */
    frac_SpF  = avg_slots_per_frame - (double)whole_SpF;
    slot_lag  = -frac_SpF;
    /*    fprintf(stderr, "frac SpF=%.3f, tot bitrate=%d kbps, s freq=%.1f kHz\n",
	  frac_SpF, bitrate[info->version][info->lay-1][info->bitrate_index],
	  s_freq[info->version][info->sampling_frequency]); */
    
    if (frac_SpF != 0)
      { /* fprintf(stderr, "Fractional number of slots, padding required\n"); */ }
    else info->padding = 0;

	#ifndef _BLADEDLL
		// AF, modified this code, so it will read to first samples into the lower halve of 
		// the buffer, this way it is easier to support the _BLADEDLL hack
		iread = get_audio(musicin,&BigBuf[0][0],&BigBuf[1][0],num_samples,fr_ps.stereo,fr_ps.header);
	#else
		// Don't read the data, it is already in the BugBuf
		{
		extern int nBladeBufferSize;
		iread=nBladeBufferSize;
		}
	#endif

	/* we are still working with short int, so we cannot devide by
	* sqrt(2) (overflow).  devide by 2, rescale later */
	if (force_ms)
	{
		// AF: Correct the lower halve of the buffer, instead of the upper halve
		for (i=0; i< 1152; i++)
		{
			double mid = (BigBuf[0][i]+BigBuf[1][i])/(2.0);
			double side= (BigBuf[0][i]-BigBuf[1][i])/(2.0);
			BigBuf[0][i]=mid;
			BigBuf[1][i]=side;

		}
	}
  }
#ifndef _BLADEDLL
  else
  {
	// AF: If not the first call, do shift the data
	// Shift the data from the upper halve of the BigBuf to to lower halve
	for (ch=0; ch<2; ch++)
		for (i=0; i<1152; i++)
			BigBuf[ch][i]=BigBuf[ch][i+1152];
  }
	// Read the sam
	iread = get_audio(musicin,&BigBuf[0][1152],&BigBuf[1][1152],num_samples,fr_ps.stereo,fr_ps.header);
#else
	{
	// Don't read the data, it is already in the BugBuf
	extern int nBladeBufferSize;
	iread=nBladeBufferSize;
	}
#endif

  if (force_ms)
    for (i=0; i< 1152; i++) {
      double mid = (BigBuf[0][1152+i]+BigBuf[1][1152+i])/(2.0);
      double side= (BigBuf[0][1152+i]-BigBuf[1][1152+i])/(2.0);
      BigBuf[0][1152+i]=mid;
      BigBuf[1][1152+i]=side;
    }

  /*  if (! iread) return 0; */
  

  /********************** process InputBuffer *****************************/
  frameNum++;
  
  if (frameNum%10==0 && !gtkflag && !silent) {
    timestatus(frameNum,totalframes,s_freq[info->version][info->sampling_frequency]);
  }

  
  if (frac_SpF != 0) {
    if (slot_lag > (frac_SpF-1.0) ) {
      slot_lag -= frac_SpF;
      info->padding = 0;
      /*  printf("No padding for this frame\n"); */
    }
    else {
      info->padding = 1;
      slot_lag += (1-frac_SpF);
      /*  printf("Padding for this frame\n");    */
    }
  }
  
  
  /***************************** Layer 3 **********************************/

  win_buf[0] = &BigBuf[0][0];
  win_buf[1] = &BigBuf[1][0];

  
  bitsPerFrame = 8 * whole_SpF + (info->padding * 8);
  mode_gr = (info->version == 1) ? 2 : 1;  /* mode_gr = 2 */
  if (mode_gr != 2) exit(-99);

  /* determine the mean bitrate for main data */
  sideinfo_len = 32;
  /*   sideinfo_len += (stereo == 1) ?  136  :  256  */
  if ( info->version == 1 )
    {   /* MPEG 1 */
      if ( stereo == 1 )
	sideinfo_len += 136;
      else
	sideinfo_len += 256;
    }
  else
    {   /* MPEG 2 */
      if ( stereo == 1 )
	sideinfo_len += 72;
      else
	sideinfo_len += 136;
    }
  mean_bits = (bitsPerFrame - sideinfo_len) / mode_gr;


  
  if (!fast) {  
  /* psychoacoustic model */
  /* this mess will be cleaned up in the future.  It is coded like this
   * to maintain original ISO and GPSYCHO psy-models
   */
  short int *bufp[2];  /* address of beginninf of left & right granule */
  gr = 0;  
  if (frameNum ==1 ) {
    /* prime the psy-model pump */
    for ( ch = 0; ch < stereo; ch++ ) 
      bufp[ch]=&BigBuf[ch][gr*576];
    L3psycho_anal( bufp,stereo,gr, info,
		   s_freq[info->version][info->sampling_frequency] * 1000.0,
		   gpsycho,check_ms_stereo,&ms_ener_ratio[0],
		   ratio.l[gr], ratio.s[gr],
		   pe[gr], blocktype);
    if (!gpsycho) {
      for ( ch = 0; ch < stereo; ch++ ) 
	l3_side.gr[gr].ch[ch].tt.block_type=blocktype[ch];
    }
  } else {
    if (!gpsycho){
      memcpy(&ratio,&ratio3,sizeof(ratio));
      for ( ch = 0; ch < stereo; ch++ ) {
	pe[gr][ch]=pe3[ch];
	l3_side.gr[0].ch[ch].tt.block_type=blocktype3[ch];
      }
    }
  }

  gr = 1;  
  for ( ch = 0; ch < stereo; ch++ ) 
    bufp[ch]=&BigBuf[ch][gr*576];
  L3psycho_anal( bufp, stereo,gr, info,
		 s_freq[info->version][info->sampling_frequency] * 1000.0,
		 gpsycho,check_ms_stereo,&ms_ener_ratio[0],
		 ratio.l[gr], ratio.s[gr],
		 pe[gr], blocktype);
  for ( ch = 0; ch < stereo; ch++ ) {
    if (gpsycho) {
      /* data returned for gr=0: memcpy */
      pe[0][ch]=pe[1][ch];
      memcpy(&ratio.l[0][ch][0],&ratio.l[1][ch][0],sizeof(ratio.l[gr][ch]));
      memcpy(&ratio.s[0][ch][0],&ratio.s[1][ch][0],sizeof(ratio.s[gr][ch]));
      l3_side.gr[0].ch[ch].tt.block_type=blocktype[ch];
    } else {
      l3_side.gr[gr].ch[ch].tt.block_type=blocktype[ch];
    }
  }
  /* do a third call */
  gr = 0;  
  for ( ch = 0; ch < stereo; ch++ ) 
    bufp[ch]=&BigBuf[ch][1152 + gr*576];
  L3psycho_anal( bufp, stereo,gr, info,
		 s_freq[info->version][info->sampling_frequency] * 1000.0,
		 gpsycho,check_ms_stereo,&ms_ener_ratio[1],
		 ratio3.l[gr], ratio3.s[gr],
		 pe3, blocktype3);
  for ( ch = 0; ch < stereo; ch++ ) {
    if (gpsycho) {
      /* data returned for gr=1, prev. frame.  memcpy */
      pe[1][ch]=pe3[ch];
      memcpy(&ratio.l[1][ch][0],&ratio3.l[0][ch][0],sizeof(ratio3.l[0][ch]));
      memcpy(&ratio.s[1][ch][0],&ratio3.s[0][ch][0],sizeof(ratio3.s[0][ch]));
      l3_side.gr[1].ch[ch].tt.block_type=blocktype3[ch];
    }
  }
  }

  /* block type flags */
  for( gr = 0; gr < mode_gr; gr++ ) {
    for ( ch = 0; ch < stereo; ch++ ) {
      gr_info *cod_info = &l3_side.gr[gr].ch[ch].tt;
      cod_info->mixed_block_flag = 0;     /* never used by this model */
      if (cod_info->block_type == NORM_TYPE )
	cod_info->window_switching_flag = 0;
      else
	cod_info->window_switching_flag = 1;
    }
  }



  
  /* polyphase filtering  */
  for( gr = 0; gr < mode_gr; gr++ )
    for ( ch = 0; ch < stereo; ch++ )
      for ( j = 0; j < 18; j++ )   {
	window_subband( &win_buf[ch], &win_que[ch][0], ch );
	filter_subband( &win_que[ch][0],  &(l3_sb_sample)[ch][gr+1][j][0] ); }
  
  /* apply mdct to the polyphase outputs */
  mdct_sub( &l3_sb_sample, xr, stereo, &l3_side, mode_gr );


  /* data was scaled by 1/2.  fix so effectively it was scaled by 1/sqrt(2) */
  if (force_ms) {
    for ( gr = 0; gr < mode_gr; gr++ )
      for ( ch = 0; ch < stereo; ch++ ) 
	for (i =0 ; i< 576; i++) 
	  xr[gr][ch][i] *= sqrt(2.0);
  }




  if (check_ms_stereo) {
    /* make sure block type is the same in each channel */
    check_ms_stereo = 
      (l3_side.gr[0].ch[0].tt.block_type==l3_side.gr[0].ch[1].tt.block_type) &&
      (l3_side.gr[1].ch[0].tt.block_type==l3_side.gr[1].ch[1].tt.block_type);
  }
  if (check_ms_stereo) {
    if ((ms_ener_ratio[0]+ms_ener_ratio[1]) < .7)  info->mode_ext = 2;  
  }


#ifdef HAVEGTK
  if (gtkflag) { 
    for ( gr = 0; gr < mode_gr; gr++ )
      for ( ch = 0; ch < stereo; ch++ ) {
	pinfo->ms_ratio[gr]=ms_ener_ratio[gr];
	/*	pinfo->pe[gr][ch]=pe[gr][ch];*/
	pinfo->blocktype[gr][ch]=
	  l3_side.gr[gr].ch[ch].tt.block_type;
	for ( j = 0; j < 576; j++ ) pinfo->xr[gr][ch][j]=xr[gr][ch][j];
      }
  }
#endif

  
  /* bit and noise allocation */
  iteration_loop( pe, ms_ener_ratio, xr, &ratio, &l3_side, l3_enc, mean_bits,
     stereo, xr_dec, &scalefac, &fr_ps, 0, bitsPerFrame, gpsycho, sfb21,
     highq );


  /* flag for our ms_stereo with psy-model on mid & side channels */
  if (force_ms) info->mode_ext = 2;



  /*  write the frame to the bitstream  */
  III_format_bitstream( bitsPerFrame, &fr_ps, l3_enc, &l3_side, 
			&scalefac, &bs, xr, NULL, 0 );


  frameBits = sstell( &bs ) - sentBits;

  
  if ( frameBits % bitsPerSlot )   /* a program failure */
    fprintf( stderr, "Sent %ld bits = %ld slots plus %ld\n",
	     frameBits, frameBits/bitsPerSlot,
	     frameBits%bitsPerSlot );
  sentBits += frameBits;


#ifdef HAVEGTK
  /* copy bit buffer into array and send to mpg123 to synthesis the pcm data */
  if (gtkflag) { 
    mpg123count=0;
    for (i=bs.buf_size-1 ; i > bs.buf_byte_idx ; (i-- ))
      mpg123bs[mpg123count++]=bs.buf[i];
    /* re-synthesize pcm data from mp3 frame */
    if (!decode1(mpg123bs,mpg123count,&mpg123pcm,stereo)) {
      if (MPGLAG == MAXMPGLAG) 
	fprintf(stderr, "MAXMPGLAG set too low.  The following frames will be mis-aligned \n");
      else MPGLAG+=1; 
    }
    pinfo->mpglag = MPGLAG;
  }

  /* empty data from bit buffer into mp3 file  */
  empty_buffer(&bs, 1+bs.buf_byte_idx);
  bs.buf[bs.buf_byte_idx] = 0;  
  
  if (gtkflag) { 
    for ( ch = 0; ch < stereo; ch++ ) {
      for ( j = 0; j < WINDELAY; j++ )
	pinfo->pcmdata[ch][j] = pinfo->pcmdata[ch][j+1152];
      for ( j = 0; j < 1152; j++ ) {
	pinfo->pcmdata[ch][j+WINDELAY] = BigBuf[ch][j];
      }
    }
    for ( ch = 0; ch < stereo; ch++ ) {
      for ( j = 0; j < DECDELAY; j++ )
	pinfo->pcmdata2[ch][j] = pinfo->pcmdata2[ch][j+1152];
      for ( j = 0; j < 1152; j++ ) {
	pinfo->pcmdata2[ch][j+DECDELAY] = mpg123pcm[ch][j];
      }
    }
    pinfo->frameNum = frameNum;
  }
#endif

  return iread/stereo;
}
 
/************************************************************************
*
* main
*
* PURPOSE:  MPEG-1 Layer III encoder, with
* psychoacoustic models ISO-2 (AT&T) and GPSYCHO
*
* SEMANTICS:  One overlapping frame of audio of up to 2 channels are
* processed at a time in the following order:
* (associated routines are in parentheses)
*
* 1.  Filter sliding window of data to get 32 subband
* samples per channel.
* (window_subband,filter_subband)
*
* 2.  Calculate scalefactors for the frame, and if layer 2,
* also calculate scalefactor select information.
* (*_scale_factor_calc)
*
* 3.  Calculate psychoacoustic masking levels using selected
* psychoacoustic model.
* (*_Psycho_One, psycho_anal)
*
* 4.  Perform iterative bit allocation for subbands with low
* mask_to_noise ratios using masking levels from step 4.
* (*_main_bit_allocation)
*
* 5.  Pack bit allocation and scalefactors onto bitstream.
* (*_encode_bit_alloc,*_encode_scale)
*
* 6.  Quantize subbands and pack them into bitstream
* (*_subband_quantization, *_sample_encoding)
*
************************************************************************/



int main(argc, argv)
int     argc;
char    **argv;
{
    
    char original_file_name[MAX_NAME_SIZE];
    char encoded_file_name[MAX_NAME_SIZE];
    static unsigned int bit_alloc[2][SBLIMIT], scfsi[2][SBLIMIT];
    static unsigned int scalar[2][3][SBLIMIT], j_scale[3][SBLIMIT];
    static double FAR ltmin[2][SBLIMIT], lgmin[2][SBLIMIT], max_sc[2][SBLIMIT];
    int model, stereo;
    unsigned long samplesPerFrame;
    layer info;
	char lpszVersion[80];

#ifdef HAVEGTK
    gtk_init (&argc, &argv);
#endif

    /* clear buffers */
    memset((char *) BigBuf, 0, sizeof(BigBuf));
    memset((char *) bit_alloc, 0, sizeof(bit_alloc));
    memset((char *) scalar, 0, sizeof(scalar));
    memset((char *) j_scale, 0, sizeof(j_scale));
    memset((char *) scfsi, 0, sizeof(scfsi));
    memset((char *) ltmin, 0, sizeof(ltmin));
    memset((char *) lgmin, 0, sizeof(lgmin));
    memset((char *) max_sc, 0, sizeof(max_sc));
    memset((char *) snr32, 0, sizeof(snr32));
    /*    memset((char *) sam, 0, sizeof(sam));*/
 
    fr_ps.header = &info;
    fr_ps.tab_num = -1;             /* no table loaded */
    fr_ps.alloc = NULL;
    info.version = MPEG_AUDIO_ID;   /* =1   Default: MPEG-1 */
    info.extension = 0;       

	/* get version string */
    if (gpsycho && !original_file_type)
		fprintf(stderr,"LAME version %s (www.sulaco.org/mp3) \n",get_lame_version(lpszVersion));


    programName = argv[0];
    if(argc==1)     /* no command-line args 
       obtain_parameters(&fr_ps, &model, &num_samples,
                         original_file_name, encoded_file_name);*/
      usage();
    else
	parse_args(argc, argv, &fr_ps, &model, &num_samples,
		   original_file_name, encoded_file_name);
    print_config(&fr_ps, &model,
                 original_file_name, encoded_file_name);
    
    hdr_to_frps(&fr_ps);
    stereo = fr_ps.stereo;
    if (gpsycho && !original_file_type) fprintf(stderr,
     "using GPSYCHO: GPL'd psycho-acoustic model \n");

#ifdef HAVEGTK
    if (gtkflag) gtkcontrol(totalframes,original_file_type,original_file_name,gpsycho); 
    else 
#endif
    while (makeframe());

    /* clean up if we were analyzing a wav file */
    if (!original_file_type) {
      if ( info.lay == 3 ) III_FlushBitstream();
      close_bit_stream_w( &bs );
      
      if (fclose(musicin) != 0){
	fprintf(stderr, "Could not close \"%s\".\n", original_file_name);
	exit(2);
      }
      frameNum--;
      timestatus(frameNum,frameNum,s_freq[info.version][info.sampling_frequency]);
      fprintf(stderr,"\n");
    }
    fflush(stderr);
    exit(0);
}
 
/************************************************************************
*
* usage
*
* PURPOSE:  Writes command line syntax to the file specified by #stderr#
*
************************************************************************/

void usage()  /* print syntax & exit */
{
  fprintf(stderr,"        LAMER Ain't an Mp3 Encoder (Reconstruction-iso)\n");
  fprintf(stderr,"        version %s\n",get_lame_version());
  fprintf(stderr,"        with GPSYCHO psycho-acoustic model (version %s). \n\n",get_psy_version());
  fprintf(stderr,"USAGE   :  %s [options] <infile> <outfile>\n",programName);
  fprintf(stderr,"OPTIONS :\n");
  fprintf(stderr,"          -m mode         (s)tereo, (j)oint, (f)orce or (m)ono  (default %c)\n",DFLT_MOD);
  fprintf(stderr,"                          force = force ms_stereo on all frames. Faster and\n");
  fprintf(stderr,"                          uses special Mid & Side masking thresholds\n");
  fprintf(stderr,"          -s sample freq  frequency of input file (kHz) - default %4.1f\n",DFLT_SFQ);
  fprintf(stderr,"          -b <bitrate>    set the bitrate, default 128kbps\n");
  fprintf(stderr,"          -x              force byte-swapping of input\n");
  fprintf(stderr,"          -a              downmix from stereo to mono file for mono encoding\n");
  fprintf(stderr,"          -e emp          de-emphasis n/5/c        (default %4c)\n",DFLT_EMP);
  fprintf(stderr,"          -c              mark as copyright\n");
  fprintf(stderr,"          -o              mark as non-original\n");
  fprintf(stderr,"          -f              fast mode (low quality)\n");
  fprintf(stderr,"          -k              disable sfb=21 cutoff\n");
  fprintf(stderr,"          -h              use (maybe) some experimental quality improvements\n");
#ifdef HAVEGTK
  fprintf(stderr,"          -g              run graphical analysis on <infile>\n");
#endif
  fprintf(stderr,"          -S              don't print progress report\n");
  fprintf(stderr,"          -O              use original (buggy) ISO psycho-acoustic model \n");
  fprintf(stderr,"\n<infile> and/or <outfile> can be \"-\", which means stdin/stdout.\n");

  exit(1);
}

/************************************************************************
*
* aiff_check
*
* PURPOSE:  Checks AIFF header information to make sure it is valid.
*           Exits if not.
*
************************************************************************/

void aiff_check( char *file_name, IFF_AIFF *pcm_aiff_data, int *version)
{
    if (pcm_aiff_data->sampleType != IFF_ID_SSND) {
       fprintf(stderr, "Sound data is not PCM in \"%s\".\n", file_name);
       exit(1);
    }

    if(SmpFrqIndex((long)pcm_aiff_data->sampleRate, version) < 0) {
       fprintf(stderr, "in \"%s\".\n", file_name);
       exit(1);
    }

    if (pcm_aiff_data->sampleSize != sizeof(short) * BITS_IN_A_BYTE) {
        fprintf(stderr, "Sound data is not %d bits in \"%s\".\n",
		sizeof(short) * BITS_IN_A_BYTE, file_name);
        exit(1);
    }

    if (pcm_aiff_data->numChannels != MONO &&
        pcm_aiff_data->numChannels != STEREO) {
       fprintf(stderr, "Sound data is not mono or stereo in \"%s\".\n",
	       file_name);
       exit(1);
    }

    if (pcm_aiff_data->blkAlgn.blockSize != 0) {
       fprintf(stderr, "Block size is not %d bytes in \"%s\".\n",
	       0, file_name);
       exit(1);
    }

    if (pcm_aiff_data->blkAlgn.offset != 0) {
       fprintf(stderr, "Block offset is not %d bytes in \"%s\".\n",
	       0, file_name);
       exit(1);
    }
}

