/* JOrbis
 * Copyright (C) 2000 ymnk, JCraft,Inc.
 *  
 * Written by: 2000 ymnk<ymnk@jcraft.com>
 *   
 * Many thanks to 
 *   Monty <monty@xiph.org> and 
 *   The XIPHOPHORUS Company http://www.xiph.org/ .
 * JOrbis has been based on their awesome works, Vorbis codec.
 *   
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public License
 * as published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
   
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Library General Public License for more details.
 * 
 * You should have received a copy of the GNU Library General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

package com.jcraft.jorbis;

public class DspState{
  static final float M_PI=3.1415926539f;
  static final int VI_TRANSFORMB=1;
  static final int VI_WINDOWB=1;

  int analysisp;
  Info vi;
  int modebits;

  float[][] pcm;
  //float[][] pcmret;
  int      pcm_storage;
  int      pcm_current;
  int      pcm_returned;

  float[]  multipliers;
  int      envelope_storage;
  int      envelope_current;

  int  eofflag;

  int lW;
  int W;
  int nW;
  int centerW;

  long granulepos;
  long sequence;

  long glue_bits;
  long time_bits;
  long floor_bits;
  long res_bits;

  // local lookup storage
//!!  Envelope ve=new Envelope(); // envelope
//float                **window[2][2][2]; // block, leadin, leadout, type
  float[][][][][] window;                 // block, leadin, leadout, type
  //vorbis_look_transform **transform[2];    // block, type 
  Object[][] transform;
  CodeBook[] fullbooks;
  // backend lookups are tied to the mode, not the backend or naked mapping
  Object[] mode;

  // local storage, only used on the encoding side.  This way the
  // application does not need to worry about freeing some packets'
  // memory and not others'; packet storage is always tracked.
  // Cleared next call to a _dsp_ function
  byte[] header;
  byte[] header1;
  byte[] header2;

  public DspState(){
    transform=new Object[2][];
    window=new float[2][][][][];
    window[0]=new float[2][][][];
    window[0][0]=new float[2][][];
    window[0][1]=new float[2][][];
    window[0][0][0]=new float[2][];
    window[0][0][1]=new float[2][];
    window[0][1][0]=new float[2][];
    window[0][1][1]=new float[2][];
    window[1]=new float[2][][][];
    window[1][0]=new float[2][][];
    window[1][1]=new float[2][][];
    window[1][0][0]=new float[2][];
    window[1][0][1]=new float[2][];
    window[1][1][0]=new float[2][];
    window[1][1][1]=new float[2][];
  }

  private static int ilog2(int v){
    int ret=0;
    while(v>1){
      ret++;
      v>>>=1;
    }
    return(ret);
  }

  static float[] window(int type, int window, int left, int right){
    float[] ret=new float[window];
    switch(type){
    case 0:
      // The 'vorbis window' (window 0) is sin(sin(x)*sin(x)*2pi)
      {
	int leftbegin=window/4-left/2;
	int rightbegin=window-window/4-right/2;
    
	for(int i=0;i<left;i++){
	  float x=(float)((i+.5)/left*M_PI/2.);
	  x=(float)Math.sin(x);
	  x*=x;
	  x*=M_PI/2.;
	  x=(float)Math.sin(x);
	  ret[i+leftbegin]=x;
	}
      
	for(int i=leftbegin+left;i<rightbegin;i++){
	  ret[i]=1.f;
	}
      
	for(int i=0;i<right;i++){
	  float x=(float)((right-i-.5)/right*M_PI/2.);
	  x=(float)Math.sin(x);
	  x*=x;
	  x*=M_PI/2.;
	  x=(float)Math.sin(x);
	  ret[i+rightbegin]=x;
	}
      }
      break;
    default:
      //free(ret);
      return(null);
    }
    return(ret);
  }

  // Analysis side code, but directly related to blocking.  Thus it's
  // here and not in analysis.c (which is for analysis transforms only).
  // The init is here because some of it is shared

  int init(Info vi, boolean encp){
//System.err.println("DspState.init: vi="+vi+", encp="+encp);
    //memset(v,0,sizeof(vorbis_dsp_state));
    this.vi=vi;
    modebits=ilog2(vi.modes);

    transform[0]=new Object[VI_TRANSFORMB];
    transform[1]=new Object[VI_TRANSFORMB];

    // MDCT is tranform 0

    transform[0][0]=new Mdct();
    transform[1][0]=new Mdct();
    ((Mdct)transform[0][0]).init(vi.blocksizes[0]);
    ((Mdct)transform[1][0]).init(vi.blocksizes[1]);

    window[0][0][0]=new float[VI_WINDOWB][];
    window[0][0][1]=window[0][0][0];
    window[0][1][0]=window[0][0][0];
    window[0][1][1]=window[0][0][0];
    window[1][0][0]=new float[VI_WINDOWB][];
    window[1][0][1]=new float[VI_WINDOWB][];
    window[1][1][0]=new float[VI_WINDOWB][];
    window[1][1][1]=new float[VI_WINDOWB][];

    for(int i=0;i<VI_WINDOWB;i++){
      window[0][0][0][i]=
	window(i,vi.blocksizes[0],vi.blocksizes[0]/2,vi.blocksizes[0]/2);
      window[1][0][0][i]=
	window(i,vi.blocksizes[1],vi.blocksizes[0]/2,vi.blocksizes[0]/2);
      window[1][0][1][i]=
	window(i,vi.blocksizes[1],vi.blocksizes[0]/2,vi.blocksizes[1]/2);
      window[1][1][0][i]=
	window(i,vi.blocksizes[1],vi.blocksizes[1]/2,vi.blocksizes[0]/2);
      window[1][1][1][i]=
	window(i,vi.blocksizes[1],vi.blocksizes[1]/2,vi.blocksizes[1]/2);
    }

//    if(encp){ // encode/decode differ here
//      // finish the codebooks
//      fullbooks=new CodeBook[vi.books];
//      for(int i=0;i<vi.books;i++){
//	fullbooks[i]=new CodeBook();
//	fullbooks[i].init_encode(vi.book_param[i]);
//      }
//      analysisp=1;
//    }
//    else{
      // finish the codebooks
      fullbooks=new CodeBook[vi.books];
      for(int i=0;i<vi.books;i++){
	fullbooks[i]=new CodeBook();
	fullbooks[i].init_decode(vi.book_param[i]);
      }
//    }

    // initialize the storage vectors to a decent size greater than the
    // minimum
  
    pcm_storage=8192; // we'll assume later that we have
                        // a minimum of twice the blocksize of
			// accumulated samples in analysis
    pcm=new float[vi.channels][];
    //pcmret=new float[vi.channels][];
    {
      for(int i=0;i<vi.channels;i++){
	pcm[i]=new float[pcm_storage];
      }
    }

    // all 1 (large block) or 0 (small block)
    // explicitly set for the sake of clarity
    lW=0; // previous window size
    W=0;  // current window size

    // all vector indexes; multiples of samples_per_envelope_step
    centerW=vi.blocksizes[1]/2;

    pcm_current=centerW;

    // initialize all the mapping/backend lookups
    mode=new Object[vi.modes];
    for(int i=0;i<vi.modes;i++){
      int mapnum=vi.mode_param[i].mapping;
      int maptype=vi.map_type[mapnum];
      mode[i]=FuncMapping.mapping_P[maptype].look(this,vi.mode_param[i], 
						  vi.map_param[mapnum]);
    }
    return(0);
  }

  public int synthesis_init(Info vi){
    init(vi, false);
    // Adjust centerW to allow an easier mechanism for determining output
    pcm_returned=centerW;
    centerW-= vi.blocksizes[W]/4+vi.blocksizes[lW]/4;
    granulepos=-1;
    sequence=-1;
    return(0);
  }

  DspState(Info vi){
    this();
    init(vi, false);
    // Adjust centerW to allow an easier mechanism for determining output
    pcm_returned=centerW;
    centerW-= vi.blocksizes[W]/4+vi.blocksizes[lW]/4;
    granulepos=-1;
    sequence=-1;
  }

  // Unike in analysis, the window is only partially applied for each
  // block.  The time domain envelope is not yet handled at the point of
  // calling (as it relies on the previous block).

  public int synthesis_blockin(Block vb){
    // Shift out any PCM/multipliers that we returned previously
    // centerW is currently the center of the last block added
    if(centerW>vi.blocksizes[1]/2 && pcm_returned>8192){
      // don't shift too much; we need to have a minimum PCM buffer of
      // 1/2 long block

      int shiftPCM=centerW-vi.blocksizes[1]/2;
      shiftPCM=(pcm_returned<shiftPCM?pcm_returned:shiftPCM);

      pcm_current-=shiftPCM;
      centerW-=shiftPCM;
      pcm_returned-=shiftPCM;
      if(shiftPCM!=0){
	for(int i=0;i<vi.channels;i++){
	  System.arraycopy(pcm[i], shiftPCM, pcm[i], 0, pcm_current);
	}
      }
    }

    lW=W;
    W=vb.W;
    nW=-1;

    glue_bits+=vb.glue_bits;
    time_bits+=vb.time_bits;
    floor_bits+=vb.floor_bits;
    res_bits+=vb.res_bits;

    if(sequence+1 != vb.sequence)granulepos=-1; // out of sequence; lose count

    sequence=vb.sequence;

    {
      int sizeW=vi.blocksizes[W];
      int _centerW=centerW+vi.blocksizes[lW]/4+sizeW/4;
      int beginW=_centerW-sizeW/2;
      int endW=beginW+sizeW;
      int beginSl=0;
      int endSl=0;

      // Do we have enough PCM/mult storage for the block?
      if(endW>pcm_storage){
	// expand the storage
	pcm_storage=endW+vi.blocksizes[1];
	for(int i=0;i<vi.channels;i++){
          float[] foo=new float[pcm_storage];
	  System.arraycopy(pcm[i], 0, foo, 0, pcm[i].length);
	  pcm[i]=foo;
	}
      }

      // overlap/add PCM
      switch(W){
      case 0:
	beginSl=0;
	endSl=vi.blocksizes[0]/2;
	break;
      case 1:
	beginSl=vi.blocksizes[1]/4-vi.blocksizes[lW]/4;
	endSl=beginSl+vi.blocksizes[lW]/2;
	break;
      }

      for(int j=0;j<vi.channels;j++){
	int _pcm=beginW;
	// the overlap/add section
	int i=0;
	for(i=beginSl;i<endSl;i++){
	  pcm[j][_pcm+i]+=vb.pcm[j][i];
	}
	// the remaining section
	for(;i<sizeW;i++){
	  pcm[j][_pcm+i]=vb.pcm[j][i];
	}
      }

      // track the frame number... This is for convenience, but also
      // making sure our last packet doesn't end with added padding.  If
      // the last packet is partial, the number of samples we'll have to
      // return will be past the vb->granulepos.
      //       
      // This is not foolproof!  It will be confused if we begin
      // decoding at the last page after a seek or hole.  In that case,
      // we don't have a starting point to judge where the last frame
      // is.  For this reason, vorbisfile will always try to make sure
      // it reads the last two marked pages in proper sequence

      if(granulepos==-1){
	granulepos=vb.granulepos;
      }
      else{
	granulepos+=(_centerW-centerW);
	if(vb.granulepos!=-1 && granulepos!=vb.granulepos){
	  if(granulepos>vb.granulepos && vb.eofflag!=0){
	    // partial last frame.  Strip the padding off
	    _centerW-=(granulepos-vb.granulepos);
	  }// else{ Shouldn't happen *unless* the bitstream is out of
           // spec.  Either way, believe the bitstream }
	  granulepos=vb.granulepos;
	}
      }

      // Update, cleanup

      centerW=_centerW;
      pcm_current=endW;
      if(vb.eofflag!=0)eofflag=1;
    }
    return(0);
  }

  // pcm==NULL indicates we just want the pending samples, no more
  public int synthesis_pcmout(float[][][] _pcm, int[] index){
    if(pcm_returned<centerW){
      if(_pcm!=null){
	for(int i=0;i<vi.channels;i++){
//	  pcmret[i]=pcm[i]+v.pcm_returned;
//!!!!!!!!
          index[i]=pcm_returned;
	}
	_pcm[0]=pcm;
      }
      return(centerW-pcm_returned);
    }
    return(0);
  }

  public int synthesis_read(int bytes){
    if(bytes!=0 && pcm_returned+bytes>centerW)return(-1);
    pcm_returned+=bytes;
    return(0);
  }

  public void clear(){
/*
    if(window[0][0][0]!=0){
      for(i=0;i<VI_WINDOWB;i++)
	if(v->window[0][0][0][i])free(v->window[0][0][0][i]);
      free(v->window[0][0][0]);

      for(j=0;j<2;j++)
	for(k=0;k<2;k++){
	  for(i=0;i<VI_WINDOWB;i++)
	    if(v->window[1][j][k][i])free(v->window[1][j][k][i]);
	  free(v->window[1][j][k]);
	}
    }
    
    if(v->pcm){
      for(i=0;i<vi->channels;i++)
	if(v->pcm[i])free(v->pcm[i]);
      free(v->pcm);
      if(v->pcmret)free(v->pcmret);
    }
    if(v->multipliers)free(v->multipliers);

    _ve_envelope_clear(&v->ve);
    if(v->transform[0]){
      mdct_clear(v->transform[0][0]);
      free(v->transform[0][0]);
      free(v->transform[0]);
    }
    if(v->transform[1]){
      mdct_clear(v->transform[1][0]);
      free(v->transform[1][0]);
      free(v->transform[1]);
    }

    // free mode lookups; these are actually vorbis_look_mapping structs
    if(vi){
      for(i=0;i<vi->modes;i++){
	int mapnum=vi->mode_param[i]->mapping;
	int maptype=vi->map_type[mapnum];
	_mapping_P[maptype]->free_look(v->mode[i]);
      }
      // free codebooks
      for(i=0;i<vi->books;i++)
	vorbis_book_clear(v->fullbooks+i);
    }

    if(v->mode)free(v->mode);    
    if(v->fullbooks)free(v->fullbooks);

    // free header, header1, header2
    if(v->header)free(v->header);
    if(v->header1)free(v->header1);
    if(v->header2)free(v->header2);

    memset(v,0,sizeof(vorbis_dsp_state));
  }
*/
}
}
