460 lines
12 KiB
Java
460 lines
12 KiB
Java
|
/* 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));
|
||
|
}
|
||
|
*/
|
||
|
}
|
||
|
}
|