456 lines
10 KiB
C#
456 lines
10 KiB
C#
|
/* 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;
|
||
|
|
||
|
namespace csvorbis
|
||
|
{
|
||
|
class Mapping0 : FuncMapping
|
||
|
{
|
||
|
//static int seq=0;
|
||
|
override public void free_info(Object imap){}
|
||
|
override public void free_look(Object imap){}
|
||
|
|
||
|
override public Object look(DspState vd, InfoMode vm, Object m)
|
||
|
{
|
||
|
Info vi=vd.vi;
|
||
|
LookMapping0 looks=new LookMapping0();
|
||
|
InfoMapping0 info=looks.map=(InfoMapping0)m;
|
||
|
looks.mode=vm;
|
||
|
|
||
|
looks.time_look=new Object[info.submaps];
|
||
|
looks.floor_look=new Object[info.submaps];
|
||
|
looks.residue_look=new Object[info.submaps];
|
||
|
|
||
|
looks.time_func=new FuncTime[info.submaps];
|
||
|
looks.floor_func=new FuncFloor[info.submaps];
|
||
|
looks.residue_func=new FuncResidue[info.submaps];
|
||
|
|
||
|
for(int i=0;i<info.submaps;i++)
|
||
|
{
|
||
|
int timenum=info.timesubmap[i];
|
||
|
int floornum=info.floorsubmap[i];
|
||
|
int resnum=info.residuesubmap[i];
|
||
|
|
||
|
looks.time_func[i]=FuncTime.time_P[vi.time_type[timenum]];
|
||
|
looks.time_look[i]=looks.time_func[i].look(vd,vm,vi.time_param[timenum]);
|
||
|
|
||
|
looks.floor_func[i]=FuncFloor.floor_P[vi.floor_type[floornum]];
|
||
|
looks.floor_look[i]=looks.floor_func[i].
|
||
|
look(vd,vm,vi.floor_param[floornum]);
|
||
|
|
||
|
looks.residue_func[i]=FuncResidue.residue_P[vi.residue_type[resnum]];
|
||
|
looks.residue_look[i]=looks.residue_func[i].
|
||
|
look(vd,vm,vi.residue_param[resnum]);
|
||
|
}
|
||
|
|
||
|
if(vi.psys!=0 && vd.analysisp!=0)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
looks.ch=vi.channels;
|
||
|
|
||
|
return(looks);
|
||
|
}
|
||
|
|
||
|
override public void pack(Info vi, Object imap, csBuffer opb)
|
||
|
{
|
||
|
InfoMapping0 info=(InfoMapping0)imap;
|
||
|
|
||
|
/* another 'we meant to do it this way' hack... up to beta 4, we
|
||
|
packed 4 binary zeros here to signify one submapping in use. We
|
||
|
now redefine that to mean four bitflags that indicate use of
|
||
|
deeper features; bit0:submappings, bit1:coupling,
|
||
|
bit2,3:reserved. This is backward compatable with all actual uses
|
||
|
of the beta code. */
|
||
|
|
||
|
if(info.submaps>1)
|
||
|
{
|
||
|
opb.write(1,1);
|
||
|
opb.write(info.submaps-1,4);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
opb.write(0,1);
|
||
|
}
|
||
|
|
||
|
if(info.coupling_steps>0)
|
||
|
{
|
||
|
opb.write(1,1);
|
||
|
opb.write(info.coupling_steps-1,8);
|
||
|
for(int i=0;i<info.coupling_steps;i++)
|
||
|
{
|
||
|
opb.write(info.coupling_mag[i],ilog2(vi.channels));
|
||
|
opb.write(info.coupling_ang[i],ilog2(vi.channels));
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
opb.write(0,1);
|
||
|
}
|
||
|
|
||
|
opb.write(0,2); /* 2,3:reserved */
|
||
|
|
||
|
/* we don't write the channel submappings if we only have one... */
|
||
|
if(info.submaps>1)
|
||
|
{
|
||
|
for(int i=0;i<vi.channels;i++)
|
||
|
opb.write(info.chmuxlist[i],4);
|
||
|
}
|
||
|
for(int i=0;i<info.submaps;i++)
|
||
|
{
|
||
|
opb.write(info.timesubmap[i],8);
|
||
|
opb.write(info.floorsubmap[i],8);
|
||
|
opb.write(info.residuesubmap[i],8);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
override public Object unpack(Info vi, csBuffer opb)
|
||
|
{
|
||
|
// also responsible for range checking
|
||
|
InfoMapping0 info=new InfoMapping0();
|
||
|
|
||
|
// !!!!
|
||
|
if(opb.read(1)!=0)
|
||
|
{
|
||
|
info.submaps=opb.read(4)+1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
info.submaps=1;
|
||
|
}
|
||
|
|
||
|
if(opb.read(1)!=0)
|
||
|
{
|
||
|
info.coupling_steps=opb.read(8)+1;
|
||
|
|
||
|
for(int i=0;i<info.coupling_steps;i++)
|
||
|
{
|
||
|
int testM=info.coupling_mag[i]=opb.read(ilog2(vi.channels));
|
||
|
int testA=info.coupling_ang[i]=opb.read(ilog2(vi.channels));
|
||
|
|
||
|
if(testM<0 ||
|
||
|
testA<0 ||
|
||
|
testM==testA ||
|
||
|
testM>=vi.channels ||
|
||
|
testA>=vi.channels)
|
||
|
{
|
||
|
//goto err_out;
|
||
|
info.free();
|
||
|
return(null);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(opb.read(2)>0)
|
||
|
{ /* 2,3:reserved */
|
||
|
//goto err_out;
|
||
|
info.free();
|
||
|
return(null);
|
||
|
}
|
||
|
|
||
|
if(info.submaps>1)
|
||
|
{
|
||
|
for(int i=0;i<vi.channels;i++)
|
||
|
{
|
||
|
info.chmuxlist[i]=opb.read(4);
|
||
|
if(info.chmuxlist[i]>=info.submaps)
|
||
|
{
|
||
|
//goto err_out;
|
||
|
info.free();
|
||
|
return(null);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for(int i=0;i<info.submaps;i++)
|
||
|
{
|
||
|
info.timesubmap[i]=opb.read(8);
|
||
|
if(info.timesubmap[i]>=vi.times)
|
||
|
{
|
||
|
//goto err_out;
|
||
|
info.free();
|
||
|
return(null);
|
||
|
}
|
||
|
info.floorsubmap[i]=opb.read(8);
|
||
|
if(info.floorsubmap[i]>=vi.floors)
|
||
|
{
|
||
|
//goto err_out;
|
||
|
info.free();
|
||
|
return(null);
|
||
|
}
|
||
|
info.residuesubmap[i]=opb.read(8);
|
||
|
if(info.residuesubmap[i]>=vi.residues)
|
||
|
{
|
||
|
//goto err_out;
|
||
|
info.free();
|
||
|
return(null);
|
||
|
}
|
||
|
}
|
||
|
return info;
|
||
|
//err_out:
|
||
|
//free_info(info);
|
||
|
//return(NULL);
|
||
|
}
|
||
|
|
||
|
|
||
|
float[][] pcmbundle=null;
|
||
|
int[] zerobundle=null;
|
||
|
int[] nonzero=null;
|
||
|
Object[] floormemo=null;
|
||
|
|
||
|
override public int inverse(Block vb, Object l)
|
||
|
{
|
||
|
lock(this)
|
||
|
{
|
||
|
//System.err.println("Mapping0.inverse");
|
||
|
DspState vd=vb.vd;
|
||
|
Info vi=vd.vi;
|
||
|
LookMapping0 look=(LookMapping0)l;
|
||
|
InfoMapping0 info=look.map;
|
||
|
InfoMode mode=look.mode;
|
||
|
int n=vb.pcmend=vi.blocksizes[vb.W];
|
||
|
|
||
|
float[] window=vd.wnd[vb.W][vb.lW][vb.nW][mode.windowtype];
|
||
|
// float[][] pcmbundle=new float[vi.channels][];
|
||
|
// int[] nonzero=new int[vi.channels];
|
||
|
if(pcmbundle==null || pcmbundle.Length<vi.channels)
|
||
|
{
|
||
|
pcmbundle=new float[vi.channels][];
|
||
|
nonzero=new int[vi.channels];
|
||
|
zerobundle=new int[vi.channels];
|
||
|
floormemo=new Object[vi.channels];
|
||
|
}
|
||
|
|
||
|
// time domain information decode (note that applying the
|
||
|
// information would have to happen later; we'll probably add a
|
||
|
// function entry to the harness for that later
|
||
|
// NOT IMPLEMENTED
|
||
|
|
||
|
// recover the spectral envelope; store it in the PCM vector for now
|
||
|
for(int i=0;i<vi.channels;i++)
|
||
|
{
|
||
|
float[] pcm=vb.pcm[i];
|
||
|
int submap=info.chmuxlist[i];
|
||
|
|
||
|
floormemo[i]=look.floor_func[submap].inverse1(vb,look.
|
||
|
floor_look[submap],
|
||
|
floormemo[i]
|
||
|
);
|
||
|
if(floormemo[i]!=null){ nonzero[i]=1; }
|
||
|
else{ nonzero[i]=0; }
|
||
|
for(int j=0; j<n/2; j++)
|
||
|
{
|
||
|
pcm[j]=0;
|
||
|
}
|
||
|
|
||
|
//_analysis_output("ifloor",seq+i,pcm,n/2,0,1);
|
||
|
}
|
||
|
|
||
|
for(int i=0; i<info.coupling_steps; i++)
|
||
|
{
|
||
|
if(nonzero[info.coupling_mag[i]]!=0 ||
|
||
|
nonzero[info.coupling_ang[i]]!=0)
|
||
|
{
|
||
|
nonzero[info.coupling_mag[i]]=1;
|
||
|
nonzero[info.coupling_ang[i]]=1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// recover the residue, apply directly to the spectral envelope
|
||
|
|
||
|
for(int i=0;i<info.submaps;i++)
|
||
|
{
|
||
|
int ch_in_bundle=0;
|
||
|
for(int j=0;j<vi.channels;j++)
|
||
|
{
|
||
|
if(info.chmuxlist[j]==i)
|
||
|
{
|
||
|
if(nonzero[j]!=0)
|
||
|
{
|
||
|
zerobundle[ch_in_bundle]=1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
zerobundle[ch_in_bundle]=0;
|
||
|
}
|
||
|
pcmbundle[ch_in_bundle++]=vb.pcm[j];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
look.residue_func[i].inverse(vb,look.residue_look[i],
|
||
|
pcmbundle,zerobundle,ch_in_bundle);
|
||
|
}
|
||
|
|
||
|
|
||
|
for(int i=info.coupling_steps-1;i>=0;i--)
|
||
|
{
|
||
|
float[] pcmM=vb.pcm[info.coupling_mag[i]];
|
||
|
float[] pcmA=vb.pcm[info.coupling_ang[i]];
|
||
|
|
||
|
for(int j=0;j<n/2;j++)
|
||
|
{
|
||
|
float mag=pcmM[j];
|
||
|
float ang=pcmA[j];
|
||
|
|
||
|
if(mag>0)
|
||
|
{
|
||
|
if(ang>0)
|
||
|
{
|
||
|
pcmM[j]=mag;
|
||
|
pcmA[j]=mag-ang;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pcmA[j]=mag;
|
||
|
pcmM[j]=mag+ang;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if(ang>0)
|
||
|
{
|
||
|
pcmM[j]=mag;
|
||
|
pcmA[j]=mag+ang;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pcmA[j]=mag;
|
||
|
pcmM[j]=mag-ang;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// /* compute and apply spectral envelope */
|
||
|
|
||
|
for(int i=0;i<vi.channels;i++)
|
||
|
{
|
||
|
float[] pcm=vb.pcm[i];
|
||
|
int submap=info.chmuxlist[i];
|
||
|
look.floor_func[submap].inverse2(vb,look.floor_look[submap],floormemo[i],pcm);
|
||
|
}
|
||
|
|
||
|
// transform the PCM data; takes PCM vector, vb; modifies PCM vector
|
||
|
// only MDCT right now....
|
||
|
|
||
|
for(int i=0;i<vi.channels;i++)
|
||
|
{
|
||
|
float[] pcm=vb.pcm[i];
|
||
|
//_analysis_output("out",seq+i,pcm,n/2,0,0);
|
||
|
((Mdct)vd.transform[vb.W][0]).backward(pcm,pcm);
|
||
|
}
|
||
|
|
||
|
// now apply the decoded pre-window time information
|
||
|
// NOT IMPLEMENTED
|
||
|
|
||
|
// window the data
|
||
|
for(int i=0;i<vi.channels;i++)
|
||
|
{
|
||
|
float[] pcm=vb.pcm[i];
|
||
|
if(nonzero[i]!=0)
|
||
|
{
|
||
|
for(int j=0;j<n;j++)
|
||
|
{
|
||
|
pcm[j]*=window[j];
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
for(int j=0;j<n;j++)
|
||
|
{
|
||
|
pcm[j]=0.0f;
|
||
|
}
|
||
|
}
|
||
|
//_analysis_output("final",seq++,pcm,n,0,0);
|
||
|
}
|
||
|
|
||
|
// now apply the decoded post-window time information
|
||
|
// NOT IMPLEMENTED
|
||
|
// all done!
|
||
|
return(0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
private static int ilog2(int v)
|
||
|
{
|
||
|
int ret=0;
|
||
|
while(v>1)
|
||
|
{
|
||
|
ret++;
|
||
|
v = (int)((uint)v >> 1);
|
||
|
}
|
||
|
return(ret);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class InfoMapping0
|
||
|
{
|
||
|
internal int submaps; // <= 16
|
||
|
internal int[] chmuxlist=new int[256]; // up to 256 channels in a Vorbis stream
|
||
|
|
||
|
internal int[] timesubmap=new int[16]; // [mux]
|
||
|
internal int[] floorsubmap=new int[16]; // [mux] submap to floors
|
||
|
internal int[] residuesubmap=new int[16];// [mux] submap to residue
|
||
|
internal int[] psysubmap=new int[16]; // [mux]; encode only
|
||
|
|
||
|
internal int coupling_steps;
|
||
|
internal int[] coupling_mag=new int[256];
|
||
|
internal int[] coupling_ang=new int[256];
|
||
|
|
||
|
internal void free()
|
||
|
{
|
||
|
chmuxlist=null;
|
||
|
timesubmap=null;
|
||
|
floorsubmap=null;
|
||
|
residuesubmap=null;
|
||
|
psysubmap=null;
|
||
|
|
||
|
coupling_mag=null;
|
||
|
coupling_ang=null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class LookMapping0
|
||
|
{
|
||
|
internal InfoMode mode;
|
||
|
internal InfoMapping0 map;
|
||
|
internal Object[] time_look;
|
||
|
internal Object[] floor_look;
|
||
|
//Object[] floor_state;
|
||
|
internal Object[] residue_look;
|
||
|
//PsyLook[] psy_look;
|
||
|
|
||
|
internal FuncTime[] time_func;
|
||
|
internal FuncFloor[] floor_func;
|
||
|
internal FuncResidue[] residue_func;
|
||
|
|
||
|
internal int ch;
|
||
|
//float[][] decay;
|
||
|
//int lastframe; // if a different mode is called, we need to
|
||
|
// invalidate decay and floor state
|
||
|
}
|
||
|
}
|