/* 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)); } */ } }