// MPEG/WAVE Sound library

//   (C) 1997 by Woo-jae Jung 

// Mpegsound.h
//   This is typeset for functions in MPEG/WAVE Sound library.
//   Now, it's for only linux-pc-?86

/************************************/
/* Include default library packages */
/************************************/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif



#include "../util/abstract/abs_thread.h"


#include "../output/outputStream.h"
#include "../input/inputPlugin.h"
#include "mpegAudioStream.h"

// the commandPipe is only needed to have better
// realtime behaviour.
// you can simply remove it.
#include "../decoder/commandPipe.h"
#include "../util/timeWrapper.h"

extern "C" {
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
// The inline code works on intel only with egcs >= 1.1
#ifdef __GNUC__
#if (__GNUC__ < 2 || ( __GNUC__ == 2 && __GNUC_MINOR__ < 91 ) )
#warning "inline code disabled! (buggy egcs version)"
#undef __NO_MATH_INLINES
#define __NO_MATH_INLINES 1
#endif
#endif

#include <math.h>

#include <stdlib.h>

}



#ifndef _L__SOUND__
#define _L__SOUND__

#include "mpegAudioHeader.h"
#include "mpegAudioBitWindow.h"

/**************************/
/* Define values for MPEG */
/**************************/
#define SCALEBLOCK         12
#define CALCBUFFERSIZE     512
#define MAXSUBBAND         32
#define MAXCHANNEL         2
#define MAXTABLE           2
#define SCALE              SHRT_MAX
#define MAXSCALE           (SHRT_MAX)
#define MINSCALE           (SHRT_MIN)
#define RAWDATASIZE        (2*2*2*32*SSLIMIT)
#define DELIVERBUFFERSIZE  32768

#define LS 0
#define RS 1

#define SSLIMIT      18
#define SBLIMIT      32



// Huffmancode
#define HTN 34



#define MODE_MONO   0
#define MODE_STEREO 1

/********************/
/* Type definitions */
/********************/
typedef float REAL;


typedef struct {
  bool         generalflag;
  unsigned int part2_3_length;
  unsigned int big_values;
  unsigned int global_gain;
  unsigned int scalefac_compress;
  unsigned int window_switching_flag;
  unsigned int block_type;
  unsigned int mixed_block_flag;
  unsigned int table_select[3];
  unsigned int subblock_gain[3];
  unsigned int region0_count;
  unsigned int region1_count;
  unsigned int preflag;
  unsigned int scalefac_scale;
  unsigned int count1table_select;
}layer3grinfo;

typedef struct {
  unsigned main_data_begin;
  unsigned private_bits;
  struct {
    unsigned scfsi[4];
    layer3grinfo gr[2];
  }ch[2];
}layer3sideinfo;

typedef struct {
  int l[23];            /* [cb] */
  int s[3][13];         /* [window][cb] */
}layer3scalefactor;     /* [ch] */

typedef struct {
  int tablename;
  unsigned int xlen,ylen;
  unsigned int linbits;
  unsigned int treelen;
  const unsigned int (*val)[2];
}HUFFMANCODETABLE;









// Class for converting mpeg format to raw format
class Mpegtoraw {
  /*****************************/
  /* Constant tables for layer */
  /*****************************/
private:
  static const int bitrate[2][3][15];
  static const int frequencies[2][3];
  static const REAL scalefactorstable[64];
  static const HUFFMANCODETABLE ht[HTN];
  static const REAL filter[512];
  REAL hcos_64[16],hcos_32[8],hcos_16[4],hcos_8[2],hcos_4;
  friend class HuffmanLookup;

  /*************************/
  /* MPEG header variables */
  /*************************/
private:


  // comes from constructor, decoder works with them
  MpegAudioStream* mpegAudioStream;
  MpegAudioHeader* mpegAudioHeader;

  /***************************************/
  /* Interface for setting music quality */
  /***************************************/
private:
  bool forcetomonoflag;
  int  downfrequency;
  int  outputstereo;
public:
  void setforcetomono(bool flag);
  void setdownfrequency(int value);
  bool getforcetomono();
  int  getdownfrequency();



  /******************************/
  /* Frame management variables */
  /******************************/
private:


  unsigned char headerBuffer[4];
  TimeStamp startAudioStamp;
  TimeStamp* lastAudioStamp;
  int lThreadRun;

  // this is a ringbuffer: 
  BufferInputStream* bufferInputStream;
  char* deliverBuffer[DELIVERBUFFERSIZE];


  /***************************************/
  /* Variables made by MPEG-Audio header */
  /***************************************/
private:

  REAL scalefactor;


  /*******************/
  /* Mpegtoraw class */
  /*******************/
public:
  Mpegtoraw(MpegAudioStream* mpegAudioStream,
	    MpegAudioHeader* mpegAudioHeader,OutputStream *output,
	    bool doFloat = false);

  ~Mpegtoraw();
  int decode(MpegAudioHeader* mpegHeaderInfo);
  void flushrawdata(void); // call after decode (write to output)

  void clearbuffer(void);


  // never call:
  void threadedplayer(void);

private:
  void initialize();
  void doPlay(char* data,int size);

  /*****************************/
  /* Loading MPEG-Audio stream */
  /*****************************/

  union
  {
    unsigned char store[4];
    unsigned int  current;
  }u;


  int getbyte()            { return mpegAudioStream->getbyte(); }
  int getbits(int bits)    { return mpegAudioStream->getbits(bits); }
  int getbits9(int bits)   { return mpegAudioStream->getbits9(bits); }
  int getbits8()           { return mpegAudioStream->getbits8(); }
  int getbit()             { return mpegAudioStream->getbit(); }
  void sync()              { mpegAudioStream->sync(); }
  bool issync()            { return mpegAudioStream->issync(); }


  /********************/
  /* Global variables */
  /********************/
private:
  int lastfrequency,laststereo;

  // for Layer3

  int layer3framestart;
  int layer3part2start;

  REAL prevblck[2][2][SBLIMIT][SSLIMIT];
  int currentprevblock;
  layer3sideinfo sideinfo;
  layer3scalefactor scalefactors[2];

  MpegAudioBitWindow bitwindow;
  MpegAudioBitWindow lastValidBitwindow;
  
  int wgetbit(void);
  int wgetbits9(int bits);
  int wgetbits(int bits);


  /*************************************/
  /* Decoding functions for each layer */
  /*************************************/
private:

  //
  // Subbandsynthesis
  //
  REAL calcbufferL[2][CALCBUFFERSIZE],calcbufferR[2][CALCBUFFERSIZE];
  int  currentcalcbuffer,calcbufferoffset;

  void computebuffer(REAL *fraction,REAL buffer[2][CALCBUFFERSIZE]);
  void generatesingle(void);
  void generate(void);
  void subbandsynthesis(REAL *fractionL,REAL *fractionR);

  void computebuffer_2(REAL *fraction,REAL buffer[2][CALCBUFFERSIZE]);
  void generatesingle_2(void);
  void generate_2(void);
  void subbandsynthesis_2(REAL *fractionL,REAL *fractionR);

  // Extarctor
  void extractlayer1(void);    // MPEG-1
  void extractlayer2(void);
  void extractlayer3(void);
  void extractlayer3_2(void);  // MPEG-2


  // Functions for layer 3
  void layer3initialize(void);
  bool layer3getsideinfo(void);
  bool layer3getsideinfo_2(void);
  void layer3getscalefactors(int ch,int gr);
  void layer3getscalefactors_2(int ch);
  void layer3huffmandecode(int ch,int gr,int out[SBLIMIT][SSLIMIT]);
  REAL layer3twopow2(int scale,int preflag,int pretab_offset,int l);
  REAL layer3twopow2_1(int a,int b,int c);
  void layer3dequantizesample(int ch,int gr,int   in[SBLIMIT][SSLIMIT],
			                    REAL out[SBLIMIT][SSLIMIT]);
  void layer3fixtostereo(int gr,REAL  in[2][SBLIMIT][SSLIMIT]);
  void layer3reorderandantialias(int ch,int gr,REAL  in[SBLIMIT][SSLIMIT],
				               REAL out[SBLIMIT][SSLIMIT]);

  void layer3hybrid(int ch,int gr,REAL in[SBLIMIT][SSLIMIT],
		                  REAL out[SSLIMIT][SBLIMIT]);
  
  void huffmandecoder_1(const HUFFMANCODETABLE *h,int *x,int *y);
  void huffmandecoder_2(const HUFFMANCODETABLE *h,int *x,int *y,int *v,int *w);


private:
  OutputStream* output;       // General playing interface
  CommandPipe* commandPipe;   // improvement for realtime behaviour
  int loutputEnabled;         // we disable playing after seek for 5 frames
  int       rawdataoffset;
  short int rawdata[RAWDATASIZE];
  float     floatdata[RAWDATASIZE];
  bool doFloat;

  void clearrawdata(void)    {rawdataoffset=0;};

  // put mpeg to raw, fix big endieness..
  void putraw(short int pcm) {
#ifdef WORDS_BIGENDIAN
    rawdata[rawdataoffset++]= ((pcm & 0xff) << 8) | ((pcm >> 8) & 0xff);
#else
    rawdata[rawdataoffset++]=pcm;
#endif
  }
  void putfloat(float sample)
  {
    if(doFloat)
    {
      floatdata[rawdataoffset++] = sample;
    }
    else
    {
      int raw=(int)(sample*scalefactor);
      if(raw>MAXSCALE)
      {
	raw=MAXSCALE;
      }
      else if(raw<MINSCALE)
      {
	raw=MINSCALE;
      }
      putraw(raw);
    }
  }

  abs_thread_t thread;
  int lInThread;

};



#endif
