445 lines
11 KiB
C#
Raw Permalink Normal View History

/* csvorbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
* Ported to C# from JOrbis by: Mark Crichton <crichton@gimp.org>
*
* Thanks go to the JOrbis team, for licencing the code under the
* LGPL, making my job a lot easier.
*
* 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.
*/
using System;
using csogg;
using csvorbis;
namespace csvorbis
{
public class DspState
{
static float M_PI=3.1415926539f;
static int VI_TRANSFORMB=1;
static int VI_WINDOWB=1;
internal int analysisp;
internal Info vi;
internal 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;
public 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
internal float[][][][][] wnd; // block, leadin, leadout, type
//vorbis_look_transform **transform[2]; // block, type
internal Object[][] transform;
internal CodeBook[] fullbooks;
// backend lookups are tied to the mode, not the backend or naked mapping
internal 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][];
wnd=new float[2][][][][];
wnd[0]=new float[2][][][];
wnd[0][0]=new float[2][][];
wnd[0][1]=new float[2][][];
wnd[0][0][0]=new float[2][];
wnd[0][0][1]=new float[2][];
wnd[0][1][0]=new float[2][];
wnd[0][1][1]=new float[2][];
wnd[1]=new float[2][][][];
wnd[1][0]=new float[2][][];
wnd[1][1]=new float[2][][];
wnd[1][0][0]=new float[2][];
wnd[1][0][1]=new float[2][];
wnd[1][1][0]=new float[2][];
wnd[1][1][1]=new float[2][];
}
private static int ilog2(int v)
{
int ret=0;
while(v>1)
{
ret++;
v = (int)((uint)v >> 1);
}
return(ret);
}
internal static float[] window(int type, int wnd, int left, int right)
{
float[] ret=new float[wnd];
switch(type)
{
case 0:
// The 'vorbis window' (window 0) is sin(sin(x)*sin(x)*2pi)
{
int leftbegin=wnd/4-left/2;
int rightbegin=wnd-wnd/4-right/2;
for(int i=0;i<left;i++)
{
float x=(float)((i+.5)/left*M_PI/2.0);
x=(float)Math.Sin(x);
x*=x;
x*=(float)(M_PI/2.0);
x=(float)Math.Sin(x);
ret[i+leftbegin]=x;
}
for(int i=leftbegin+left;i<rightbegin;i++)
{
ret[i]=1.0f;
}
for(int i=0;i<right;i++)
{
float x=(float)((right-i-.5)/right*M_PI/2.0);
x=(float)Math.Sin(x);
x*=x;
x*=(float)(M_PI/2.0);
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, bool 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]);
wnd[0][0][0]=new float[VI_WINDOWB][];
wnd[0][0][1]=wnd[0][0][0];
wnd[0][1][0]=wnd[0][0][0];
wnd[0][1][1]=wnd[0][0][0];
wnd[1][0][0]=new float[VI_WINDOWB][];
wnd[1][0][1]=new float[VI_WINDOWB][];
wnd[1][1][0]=new float[VI_WINDOWB][];
wnd[1][1][1]=new float[VI_WINDOWB][];
for(int i=0;i<VI_WINDOWB;i++)
{
wnd[0][0][0][i]=
window(i,vi.blocksizes[0],vi.blocksizes[0]/2,vi.blocksizes[0]/2);
wnd[1][0][0][i]=
window(i,vi.blocksizes[1],vi.blocksizes[0]/2,vi.blocksizes[0]/2);
wnd[1][0][1][i]=
window(i,vi.blocksizes[1],vi.blocksizes[0]/2,vi.blocksizes[1]/2);
wnd[1][1][0][i]=
window(i,vi.blocksizes[1],vi.blocksizes[1]/2,vi.blocksizes[0]/2);
wnd[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++)
{
Array.Copy(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];
Array.Copy(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 = _centerW - (int)(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()
{
}
}
}