
/*
 *  Changes from version 1.1 to 1.2:
 *    - minor changes to create a LinuxObuffer object
 *    - minor changes for a u-law version, which creates 8 kHz u-law output
 *      on an amd device or in stdout mode, if compiled with ULAW defined
 *    - option -amd forces maplay to treat /dev/audio as an amd device
 *      in the u-law version. This is helpful on some SPARC clones.
 *    - iostreams manipulator calls like "cerr << setw (2) << ..." replaced by
 *      "cerr.width (2); ..." due to problems with older GNU C++ releases.
 */

/*
 * Playlists added and other minor changes by Timo Jantunen (jeti@iki.fi).
 *
 * Added MPEG-1 Layer III and MPEG-2 routines from maplay 1.2+ 1.81 for
 * Windows by Jeff Tsay.
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "all.h"
#include "crc.h"
#include "header.h"
#include "ibitstream.h"
#include "obuffer.h"
#include "subband.h"
#include "subband_layer_1.h"
#include "subband_layer_2.h"
#include "synthesis_filter.h"
#include "layer3.h"
#include <ctype.h>
#include <time.h>

// space for storing file names & comments
#define MAXFILES 1024
#define BIGBSIZE 80*MAXFILES
static char *fname[MAXFILES],*fcomment[MAXFILES];
//,fnbuffer[BIGBSIZE];
static char *nullcomment="";
int numfiles=0;

// data extracted from commandline arguments:
static bool verbose_mode = FALSE, quiet_mode = FALSE, filter_check = FALSE;
static bool stdout_mode = FALSE, random_order = FALSE;
static enum e_channels which_channels = both, which_channels_save=both;
static bool use_speaker = FALSE, use_headphone = FALSE, use_line_out = FALSE;

static bool use_own_scalefactor = FALSE;
static real scalefactor;

// objects needed for playing a file:
static Ibitstream *stream;
static Crc16 *crc;
SynthesisFilter *filter1 = NULL, *filter2 = NULL;
Obuffer *buffer = NULL;
Header *header = NULL;
Subband *subbands[32] = {NULL, NULL, NULL, NULL,
			 NULL, NULL, NULL, NULL,
			 NULL, NULL, NULL, NULL,
			 NULL, NULL, NULL, NULL,
			 NULL, NULL, NULL, NULL,
			 NULL, NULL, NULL, NULL,
			 NULL, NULL, NULL, NULL,
			 NULL, NULL, NULL, NULL};

LayerIII_Decoder *l3decoder = NULL;

static void Exit (int returncode)
  // delete some objects and exit
{
  unsigned int i;

  if ((returncode == 1) && buffer) {
    if (header)
      for (i = 0; i < header->number_of_subbands (); ++i) {
	delete subbands[i];
	subbands[i] = NULL;
      }
  }

  delete buffer;	// delete on NULL-pointers are harmless
  delete filter1;
  delete filter2;
  delete header;
  delete stream;

  exit (returncode);
}


int main (int argc, char *argv[])
{
  Subband *subbands[32];

  uint32 layer;
  e_mode mode;
  e_sample_frequency sample_frequency, old_freq;
  bool stereo=FALSE, old_stereo=TRUE;

  bool read_ready = FALSE, write_ready = FALSE;
  uint32 i;
  int fnro;

  if (argc < 2 || !strncmp (argv[1], "-h", 2))
  {
usage:
fprintf(stderr,"usage error\n");
#if 0
    cerr << "\nusage: " << argv[0]
	 << " [option(s)] [filename(s)]\n"
	    "  filename   filename of a MPEG audio stream or - for stdin\n"
	    "  -v         verbose mode\n"
	    "  -q         quiet mode (don't print filenames, when playing multiple files)\n"

	    "  -s         write pcm samples to stdout\n"
	    "  -l         decode only the left channel\n"
	    "  -r         decode only the right channel\n"
	    "  -c         check for filter range violations\n"
	    "  -f ushort  use this scalefactor instead of the default value 32768\n"
	    "  -@ list    get files (and comments) from file 'list'\n"
	    "  -a         randomize list order\n\n"
	    "@(#) MPEG Audio Player maplay3 1.1 "
	    "("
	    "Linux"
	    " version)\n"
	    "@(#) Copyright (C) 1993, 1994 Tobias Bading (bading@cs.tu-berlin.de)\n"
	    "@(#) Berlin University of Technology\n"
	    "@(#) Created: 6/23/94 14:12:46\n"
	    "@(#) This program is free software. See the GNU General Public License\n"
	    "@(#) in the file COPYING for more details.\n"
	    "@(#) Layer III routines Copyright 1991 MPEG/audio software simulation group\n"
	    "@(#) and Jeff Tsay (ctsay@pasteur.eecs.berkeley.edu).\n"
	    "@(#) Playlist modifications and integration of Layer III routines\n"
	    "@(#) by Timo Jantunen (jeti@iki.fi).\n"
            "@(#) Compiled " << __TIME__ << " " << __DATE__ << "\n";
#endif
    exit (0);
  }
  // parse arguments:
  for (i = 1; i < (unsigned int)argc; ++i)
    if (argv[i][0] == '-' && argv[i][1])
      switch ((int)argv[i][1])
      {
	case 'v':
	  verbose_mode = TRUE;
	  quiet_mode = FALSE;
	  break;
	case 'q':
	  quiet_mode = TRUE;
	  verbose_mode = FALSE;
	  break;
	case 's':
	  stdout_mode = TRUE;
	  break;
	case 'l':
	  which_channels = left;
	  break;
	case 'r':
	  which_channels = right;
	  break;
	case 'c':
	  filter_check = TRUE;
	  break;
	case 'f':
	  if (++i == (uint32)argc)
	  {
; //	    cerr << "please specify a new scalefactor after the -f option!\n";
	    exit (1);
	  }
	  use_own_scalefactor = TRUE;
	  sscanf (argv[i], "%f", &scalefactor);
	  break;
	case '@':
	  if (++i ==(uint32) argc)
	  {
	    fprintf(stderr,"please specify a filename after the -@ option!\n");
	    exit (1);
	  }
//	  addPlaylist(argv[i]);
	  break;
	case 'a':
	  random_order = TRUE;
	  break;
	default:
	  goto usage;
      }
      else if (numfiles==MAXFILES) {
        fprintf(stderr,"Too many files! Maximum is %d\n",MAXFILES);
        exit(0);
      }        
      else {
        fcomment[numfiles]=nullcomment;
        fname[numfiles++]=argv[i];
      }

  if(numfiles==0)
    goto usage;

  if (!(use_speaker || use_headphone || use_line_out))
    use_speaker = TRUE;

  if(!quiet_mode && numfiles>1)
    fprintf(stderr,"Playing %d files...\n",numfiles);

  // randomize order
#if 0
  if(numfiles>1 && random_order) {
    int t1,t2,nro;char *t;
    srand(time(NULL)*101);
    for(fnro=0;fnro<numfiles*17;fnro++) {
      t1=(rand()/13)%numfiles;t2=fnro%numfiles;
      t=   fname[t1];   fname[t1]=   fname[t2];   fname[t2]=t;
      t=fcomment[t1];fcomment[t1]=fcomment[t2];fcomment[t2]=t;
    }
  }
#endif

  which_channels_save=which_channels; // needed to remember original settings
  // if there is a mono stream in the middle of playlist...

// ******** BEGIN MULTIPLE FILE LOOP *******
// ugly bastard, but works... I think.
  for(fnro=0;fnro<numfiles;fnro++) {

  if (!strcmp (fname[fnro], "-"))
    stream = new Ibitstream (0);		// read from stdin
  else
    stream = new Ibitstream (fname[fnro]);	// read from file
  header = new Header;
  if (!header->read_header (stream, &crc))
  {
   fprintf(stderr,"bad header!\n");
    Exit (1);
  }

  // get info from header of first frame:
  layer = header->layer();
  if ((mode = header->mode()) == single_channel)
    which_channels = left;
  else
    which_channels=which_channels_save;

  sample_frequency = header->sample_frequency();

  // create filter(s):

  if (use_own_scalefactor)
    filter1 = new SynthesisFilter (0, scalefactor);
  else
    filter1 = new SynthesisFilter (0);
  if (mode != single_channel && which_channels == both)
    if (use_own_scalefactor)
      filter2 = new SynthesisFilter (1, scalefactor);
    else
      filter2 = new SynthesisFilter (1);

  // this mpeg stream has same sample frequency and number of channels as last one?
  stereo=(mode == single_channel || which_channels != both)? FALSE:TRUE;
  if(buffer!=NULL && (old_freq!=sample_frequency || old_stereo!=stereo)) {
    delete buffer;
    buffer=NULL;
    //cerr << "deleted obuffer\n";
  }
  old_freq=sample_frequency;old_stereo=stereo;

  // create buffer:
  if(buffer==NULL) // if it don't already exists
  if (stdout_mode)
    if (mode == single_channel || which_channels != both)
      buffer = new FileObuffer (1);
    else
      buffer = new FileObuffer (2);
  else


#ifdef LINUX
  {
    if (LinuxObuffer::class_suitable (mode == single_channel || which_channels != both) ? 1 : 2)
      if (mode == single_channel || which_channels != both)
	buffer = new LinuxObuffer (1, header);
      else
	buffer = new LinuxObuffer (2, header);
    else
      Exit (0);
  }
#else
{
jhg
;
}
#endif	// !LINUX


  char *name = strrchr (fname[fnro], '/');
  if (name)
    ++name;
  else
    name = fname[fnro];

  if(!quiet_mode && numfiles>1) {
    if(fcomment[fnro]!=nullcomment)
      fprintf(stderr,"%s %s\n",name,fcomment[fnro]);
    else
      if(!verbose_mode) fprintf(stderr,"%s\n",name);
  }

  if (verbose_mode)
  {
    // print informations about the stream
    ; // cerr << name << " is a layer " << header->layer_string () << ' '
//	 << header->mode_string () << ' ' << header->version_string() << " audio stream with";
    if (!header->checksums ()) fprintf(stderr,"%s       %s\n",header->bitrate_string (), header->sample_frequency_string ());
    if (header->original ()) fprintf(stderr,"orig");
    else fprintf(stderr,"copy");
    if (!header->copyright ()) fprintf(stderr,"copyr");

  }

  // Layer III : initialize decoder

  if (layer == 3)
    l3decoder = new LayerIII_Decoder(stream, header, filter1, filter2, buffer, which_channels);

  do
  {
    // is there a change in important parameters?
    // (bitrate switching is allowed)
    if (header->layer () != layer)
    {
      // layer switching is allowed

      if (header->layer() == 3) {
	l3decoder = new LayerIII_Decoder(stream, header, filter1, filter2, buffer, which_channels);

      } else if (layer == 3) {
	delete l3decoder;
	l3decoder = NULL;
      }

      if (verbose_mode)
fprintf(stderr,"switching to layer %s\n",header->layer_string ());

      layer = header->layer ();
    }
    if ((mode == single_channel && header->mode () != single_channel) ||
	(mode != single_channel && header->mode () == single_channel))
    {
      // switching from single channel to stereo or vice versa is not allowed
fprintf(stderr,"illegal switch from single channel to stereo or vice versa!\n");
      Exit (1);
    }
    if (header->sample_frequency () != sample_frequency)
    {
      // switching the sample frequency is not allowed
      ; // cerr << "sorry, can't switch the sample frequency in the middle of the stream!\n";
      Exit (1);
    }

    if (header->layer() != 3) { // Layer III

      // create subband objects:
      if (header->layer () == 1)
	{
	  if (header->mode () == single_channel)
	    for (i = 0; i < header->number_of_subbands (); ++i)
	      subbands[i] = new SubbandLayer1 (i);
	  else if (header->mode () == joint_stereo)
	    {
	      for (i = 0; i < header->intensity_stereo_bound (); ++i)
		subbands[i] = new SubbandLayer1Stereo (i);
	      for (; i < header->number_of_subbands (); ++i)
		subbands[i] = new SubbandLayer1IntensityStereo (i);
	    }
	  else
	    for (i = 0; i < header->number_of_subbands (); ++i)
	      subbands[i] = new SubbandLayer1Stereo (i);
	}
      else if (header->layer () == 2)
	{
	  if (header->mode () == single_channel)
	    for (i = 0; i < header->number_of_subbands (); ++i)
	      subbands[i] = new SubbandLayer2 (i);
	  else if (header->mode () == joint_stereo)
	    {
	      for (i = 0; i < header->intensity_stereo_bound (); ++i)
		subbands[i] = new SubbandLayer2Stereo (i);
	      for (; i < header->number_of_subbands (); ++i)
		subbands[i] = new SubbandLayer2IntensityStereo (i);
	    }
	  else
	    for (i = 0; i < header->number_of_subbands (); ++i)
	      subbands[i] = new SubbandLayer2Stereo (i);
	}

      // start to read audio data:
      for (i = 0; i < header->number_of_subbands (); ++i)
	subbands[i]->read_allocation (stream, header, crc);
      
      if (header->layer () == 2)
	for (i = 0; i < header->number_of_subbands (); ++i)
	  ((SubbandLayer2 *)subbands[i])->read_scalefactor_selection (stream, crc);

      if (!crc || header->checksum_ok ())
	{
	  // no checksums or checksum ok, continue reading from stream:
	  for (i = 0; i < header->number_of_subbands (); ++i)
	    subbands[i]->read_scalefactor (stream, header);

	  do
	    {
	      for (i = 0; i < header->number_of_subbands (); ++i)
		read_ready = subbands[i]->read_sampledata (stream);

	      do
		{
		  for (i = 0; i < header->number_of_subbands (); ++i)
		    write_ready = subbands[i]->put_next_sample (which_channels, filter1, filter2);

		  filter1->calculate_pcm_samples (buffer);
		  if (which_channels == both && header->mode () != single_channel)
		    filter2->calculate_pcm_samples (buffer);
		} while (!write_ready);
	    } while (!read_ready);

	  buffer->write_buffer (1);		// write to stdout
	}
      else
	// Sh*t! Wrong crc checksum in frame!
	; // cerr << "WARNING: frame contains wrong crc checksum! (throwing frame away)\n";

      for (i = 0; i < header->number_of_subbands (); ++i) {
	delete subbands[i];
	subbands[i] = NULL;
      }

    } else {  // Layer III
      l3decoder->decode();
    }
  }
  while (header->read_header (stream, &crc));

  uint32 range_violations = filter1->violations ();
  if (mode != single_channel && which_channels == both)
   range_violations += filter2->violations ();

  if (filter_check)
  {
    // check whether (one of) the filter(s) produced values not in [-1.0, 1.0]:
    if (range_violations)
    {
      ; // cerr << range_violations << " range violations have occured!\n";
      if (stdout_mode)
	; // cerr << "If you notice these violations,\n";
      else
	; // cerr << "If you have noticed these violations,\n";
      ; // cerr << "please use the -f option with the value ";
      if (mode != single_channel && which_channels == both &&
	  filter2->hardest_violation () > filter1->hardest_violation ())
	; // cerr << filter2->recommended_scalefactor ();
      else
	; // cerr << filter1->recommended_scalefactor ();
      ; // cerr << "\nor a greater value up to 32768 and try again.\n";
    }
  }

  if (verbose_mode)
  {
    // print playtime of stream:
    //real playtime = filter1->seconds_played (header->frequency ());
   // uint32 minutes = (uint32)(playtime / 60.0);
   // uint32 seconds = (uint32)playtime - minutes * 60;
    //uint32 centiseconds = (uint32)((playtime - (real)(minutes * 60) - (real)seconds) * 100.0);
    ; // cerr << "end of stream, playtime: " << minutes << ':';
    ; // cerr.width (2);
    ; // cerr.fill ('0');
    ; // cerr << seconds << '.';
    ; // cerr.width (2);
    ; // cerr.fill ('0');
    ; // cerr << centiseconds << '\n';
  }

  //delete buffer;
  //buffer = NULL;

  delete filter1;
  filter1 = NULL;
  delete filter2;
  filter2 = NULL;
  delete l3decoder;
  l3decoder = NULL;

  delete stream;
  stream=NULL;
  delete header;
  header=NULL;

  } // for fnro (multifile loop)

  delete buffer;
  return 0;
}
