rockbox/songdbj/com/jcraft/jorbis/StaticCodeBook.java
Michiel Van Der Kolk 9fee0ec4ca Songdb java version, source. only 1.5 compatible
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7101 a1c6a512-1295-4272-9138-f99709370657
2005-07-11 15:42:37 +00:00

588 lines
16 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;
import com.jcraft.jogg.*;
class StaticCodeBook{
int dim; // codebook dimensions (elements per vector)
int entries; // codebook entries
int[] lengthlist; // codeword lengths in bits
// mapping
int maptype; // 0=none
// 1=implicitly populated values from map column
// 2=listed arbitrary values
// The below does a linear, single monotonic sequence mapping.
int q_min; // packed 32 bit float; quant value 0 maps to minval
int q_delta; // packed 32 bit float; val 1 - val 0 == delta
int q_quant; // bits: 0 < quant <= 16
int q_sequencep; // bitflag
// additional information for log (dB) mapping; the linear mapping
// is assumed to actually be values in dB. encodebias is used to
// assign an error weight to 0 dB. We have two additional flags:
// zeroflag indicates if entry zero is to represent -Inf dB; negflag
// indicates if we're to represent negative linear values in a
// mirror of the positive mapping.
int[] quantlist; // map == 1: (int)(entries/dim) element column map
// map == 2: list of dim*entries quantized entry vals
// encode helpers
EncodeAuxNearestMatch nearest_tree;
EncodeAuxThreshMatch thresh_tree;
StaticCodeBook(){}
StaticCodeBook(int dim, int entries, int[] lengthlist,
int maptype, int q_min, int q_delta,
int q_quant, int q_sequencep, int[] quantlist,
//EncodeAuxNearestmatch nearest_tree,
Object nearest_tree,
// EncodeAuxThreshmatch thresh_tree,
Object thresh_tree
){
this();
this.dim=dim; this.entries=entries; this.lengthlist=lengthlist;
this.maptype=maptype; this.q_min=q_min; this.q_delta=q_delta;
this.q_quant=q_quant; this.q_sequencep=q_sequencep;
this.quantlist=quantlist;
}
int pack(Buffer opb){
int i;
boolean ordered=false;
opb.write(0x564342,24);
opb.write(dim, 16);
opb.write(entries, 24);
// pack the codewords. There are two packings; length ordered and
// length random. Decide between the two now.
for(i=1;i<entries;i++){
if(lengthlist[i]<lengthlist[i-1])break;
}
if(i==entries)ordered=true;
if(ordered){
// length ordered. We only need to say how many codewords of
// each length. The actual codewords are generated
// deterministically
int count=0;
opb.write(1,1); // ordered
opb.write(lengthlist[0]-1,5); // 1 to 32
for(i=1;i<entries;i++){
int _this=lengthlist[i];
int _last=lengthlist[i-1];
if(_this>_last){
for(int j=_last;j<_this;j++){
opb.write(i-count,ilog(entries-count));
count=i;
}
}
}
opb.write(i-count,ilog(entries-count));
}
else{
// length random. Again, we don't code the codeword itself, just
// the length. This time, though, we have to encode each length
opb.write(0,1); // unordered
// algortihmic mapping has use for 'unused entries', which we tag
// here. The algorithmic mapping happens as usual, but the unused
// entry has no codeword.
for(i=0;i<entries;i++){
if(lengthlist[i]==0)break;
}
if(i==entries){
opb.write(0,1); // no unused entries
for(i=0;i<entries;i++){
opb.write(lengthlist[i]-1,5);
}
}
else{
opb.write(1,1); // we have unused entries; thus we tag
for(i=0;i<entries;i++){
if(lengthlist[i]==0){
opb.write(0,1);
}
else{
opb.write(1,1);
opb.write(lengthlist[i]-1,5);
}
}
}
}
// is the entry number the desired return value, or do we have a
// mapping? If we have a mapping, what type?
opb.write(maptype,4);
switch(maptype){
case 0:
// no mapping
break;
case 1:
case 2:
// implicitly populated value mapping
// explicitly populated value mapping
if(quantlist==null){
// no quantlist? error
return(-1);
}
// values that define the dequantization
opb.write(q_min,32);
opb.write(q_delta,32);
opb.write(q_quant-1,4);
opb.write(q_sequencep,1);
{
int quantvals=0;
switch(maptype){
case 1:
// a single column of (c->entries/c->dim) quantized values for
// building a full value list algorithmically (square lattice)
quantvals=maptype1_quantvals();
break;
case 2:
// every value (c->entries*c->dim total) specified explicitly
quantvals=entries*dim;
break;
}
// quantized values
for(i=0;i<quantvals;i++){
opb.write(Math.abs(quantlist[i]),q_quant);
}
}
break;
default:
// error case; we don't have any other map types now
return(-1);
}
return(0);
}
/*
*/
// unpacks a codebook from the packet buffer into the codebook struct,
// readies the codebook auxiliary structures for decode
int unpack(Buffer opb){
int i;
//memset(s,0,sizeof(static_codebook));
// make sure alignment is correct
if(opb.read(24)!=0x564342){
// goto _eofout;
clear();
return(-1);
}
// first the basic parameters
dim=opb.read(16);
entries=opb.read(24);
if(entries==-1){
// goto _eofout;
clear();
return(-1);
}
// codeword ordering.... length ordered or unordered?
switch(opb.read(1)){
case 0:
// unordered
lengthlist=new int[entries];
// allocated but unused entries?
if(opb.read(1)!=0){
// yes, unused entries
for(i=0;i<entries;i++){
if(opb.read(1)!=0){
int num=opb.read(5);
if(num==-1){
// goto _eofout;
clear();
return(-1);
}
lengthlist[i]=num+1;
}
else{
lengthlist[i]=0;
}
}
}
else{
// all entries used; no tagging
for(i=0;i<entries;i++){
int num=opb.read(5);
if(num==-1){
// goto _eofout;
clear();
return(-1);
}
lengthlist[i]=num+1;
}
}
break;
case 1:
// ordered
{
int length=opb.read(5)+1;
lengthlist=new int[entries];
for(i=0;i<entries;){
int num=opb.read(ilog(entries-i));
if(num==-1){
// goto _eofout;
clear();
return(-1);
}
for(int j=0;j<num;j++,i++){
lengthlist[i]=length;
}
length++;
}
}
break;
default:
// EOF
return(-1);
}
// Do we have a mapping to unpack?
switch((maptype=opb.read(4))){
case 0:
// no mapping
break;
case 1:
case 2:
// implicitly populated value mapping
// explicitly populated value mapping
q_min=opb.read(32);
q_delta=opb.read(32);
q_quant=opb.read(4)+1;
q_sequencep=opb.read(1);
{
int quantvals=0;
switch(maptype){
case 1:
quantvals=maptype1_quantvals();
break;
case 2:
quantvals=entries*dim;
break;
}
// quantized values
quantlist=new int[quantvals];
for(i=0;i<quantvals;i++){
quantlist[i]=opb.read(q_quant);
}
if(quantlist[quantvals-1]==-1){
// goto _eofout;
clear();
return(-1);
}
}
break;
default:
// goto _eofout;
clear();
return(-1);
}
// all set
return(0);
// _errout:
// _eofout:
// vorbis_staticbook_clear(s);
// return(-1);
}
// there might be a straightforward one-line way to do the below
// that's portable and totally safe against roundoff, but I haven't
// thought of it. Therefore, we opt on the side of caution
private int maptype1_quantvals(){
int vals=(int)(Math.floor(Math.pow(entries,1./dim)));
// the above *should* be reliable, but we'll not assume that FP is
// ever reliable when bitstream sync is at stake; verify via integer
// means that vals really is the greatest value of dim for which
// vals^b->bim <= b->entries
// treat the above as an initial guess
while(true){
int acc=1;
int acc1=1;
for(int i=0;i<dim;i++){
acc*=vals;
acc1*=vals+1;
}
if(acc<=entries && acc1>entries){ return(vals); }
else{
if(acc>entries){ vals--; }
else{ vals++; }
}
}
}
void clear(){
// if(quantlist!=null)free(b->quantlist);
// if(lengthlist!=null)free(b->lengthlist);
// if(nearest_tree!=null){
// free(b->nearest_tree->ptr0);
// free(b->nearest_tree->ptr1);
// free(b->nearest_tree->p);
// free(b->nearest_tree->q);
// memset(b->nearest_tree,0,sizeof(encode_aux_nearestmatch));
// free(b->nearest_tree);
// }
// if(thresh_tree!=null){
// free(b->thresh_tree->quantthresh);
// free(b->thresh_tree->quantmap);
// memset(b->thresh_tree,0,sizeof(encode_aux_threshmatch));
// free(b->thresh_tree);
// }
// memset(b,0,sizeof(static_codebook));
}
// unpack the quantized list of values for encode/decode
// we need to deal with two map types: in map type 1, the values are
// generated algorithmically (each column of the vector counts through
// the values in the quant vector). in map type 2, all the values came
// in in an explicit list. Both value lists must be unpacked
float[] unquantize(){
if(maptype==1 || maptype==2){
int quantvals;
float mindel=float32_unpack(q_min);
float delta=float32_unpack(q_delta);
float[] r=new float[entries*dim];
//System.err.println("q_min="+q_min+", mindel="+mindel);
// maptype 1 and 2 both use a quantized value vector, but
// different sizes
switch(maptype){
case 1:
// most of the time, entries%dimensions == 0, but we need to be
// well defined. We define that the possible vales at each
// scalar is values == entries/dim. If entries%dim != 0, we'll
// have 'too few' values (values*dim<entries), which means that
// we'll have 'left over' entries; left over entries use zeroed
// values (and are wasted). So don't generate codebooks like that
quantvals=maptype1_quantvals();
for(int j=0;j<entries;j++){
float last=0.f;
int indexdiv=1;
for(int k=0;k<dim;k++){
int index=(j/indexdiv)%quantvals;
float val=quantlist[index];
val=Math.abs(val)*delta+mindel+last;
if(q_sequencep!=0)last=val;
r[j*dim+k]=val;
indexdiv*=quantvals;
}
}
break;
case 2:
for(int j=0;j<entries;j++){
float last=0.f;
for(int k=0;k<dim;k++){
float val=quantlist[j*dim+k];
//if((j*dim+k)==0){System.err.println(" | 0 -> "+val+" | ");}
val=Math.abs(val)*delta+mindel+last;
if(q_sequencep!=0)last=val;
r[j*dim+k]=val;
//if((j*dim+k)==0){System.err.println(" $ r[0] -> "+r[0]+" | ");}
}
}
//System.err.println("\nr[0]="+r[0]);
}
return(r);
}
return(null);
}
private static int ilog(int v){
int ret=0;
while(v!=0){
ret++;
v>>>=1;
}
return(ret);
}
// 32 bit float (not IEEE; nonnormalized mantissa +
// biased exponent) : neeeeeee eeemmmmm mmmmmmmm mmmmmmmm
// Why not IEEE? It's just not that important here.
static final int VQ_FEXP=10;
static final int VQ_FMAN=21;
static final int VQ_FEXP_BIAS=768; // bias toward values smaller than 1.
// doesn't currently guard under/overflow
static long float32_pack(float val){
int sign=0;
int exp;
int mant;
if(val<0){
sign=0x80000000;
val= -val;
}
exp=(int)Math.floor(Math.log(val)/Math.log(2));
mant=(int)Math.rint(Math.pow(val,(VQ_FMAN-1)-exp));
exp=(exp+VQ_FEXP_BIAS)<<VQ_FMAN;
return(sign|exp|mant);
}
static float float32_unpack(int val){
float mant=val&0x1fffff;
float sign=val&0x80000000;
float exp =(val&0x7fe00000)>>>VQ_FMAN;
//System.err.println("mant="+mant+", sign="+sign+", exp="+exp);
//if(sign!=0.0)mant= -mant;
if((val&0x80000000)!=0)mant= -mant;
//System.err.println("mant="+mant);
return(ldexp(mant,((int)exp)-(VQ_FMAN-1)-VQ_FEXP_BIAS));
}
static float ldexp(float foo, int e){
return (float)(foo*Math.pow(2, e));
}
/*
// TEST
// Unit tests of the dequantizer; this stuff will be OK
// cross-platform, I simply want to be sure that special mapping cases
// actually work properly; a bug could go unnoticed for a while
// cases:
//
// no mapping
// full, explicit mapping
// algorithmic mapping
//
// nonsequential
// sequential
static int[] full_quantlist1={0,1,2,3, 4,5,6,7, 8,3,6,1};
static int[] partial_quantlist1={0,7,2};
// no mapping
static StaticCodeBook test1=new StaticCodeBook(4,16,null,
0,0,0,0,0,
null,null,null);
static float[] test1_result=null;
// linear, full mapping, nonsequential
static StaticCodeBook test2=new StaticCodeBook(4,3,null,
2,-533200896,1611661312,4,0,
full_quantlist1, null, null);
static float[] test2_result={-3,-2,-1,0, 1,2,3,4, 5,0,3,-2};
// linear, full mapping, sequential
static StaticCodeBook test3=new StaticCodeBook(4,3,null,
2, -533200896,1611661312,4,1,
full_quantlist1,null, null);
static float[] test3_result={-3,-5,-6,-6, 1,3,6,10, 5,5,8,6};
// linear, algorithmic mapping, nonsequential
static StaticCodeBook test4=new StaticCodeBook(3,27,null,
1,-533200896,1611661312,4,0,
partial_quantlist1,null,null);
static float[] test4_result={-3,-3,-3, 4,-3,-3, -1,-3,-3,
-3, 4,-3, 4, 4,-3, -1, 4,-3,
-3,-1,-3, 4,-1,-3, -1,-1,-3,
-3,-3, 4, 4,-3, 4, -1,-3, 4,
-3, 4, 4, 4, 4, 4, -1, 4, 4,
-3,-1, 4, 4,-1, 4, -1,-1, 4,
-3,-3,-1, 4,-3,-1, -1,-3,-1,
-3, 4,-1, 4, 4,-1, -1, 4,-1,
-3,-1,-1, 4,-1,-1, -1,-1,-1};
// linear, algorithmic mapping, sequential
static StaticCodeBook test5=new StaticCodeBook(3,27,null,
1,-533200896,1611661312,4,1,
partial_quantlist1,null,null);
static float[] test5_result={-3,-6,-9, 4, 1,-2, -1,-4,-7,
-3, 1,-2, 4, 8, 5, -1, 3, 0,
-3,-4,-7, 4, 3, 0, -1,-2,-5,
-3,-6,-2, 4, 1, 5, -1,-4, 0,
-3, 1, 5, 4, 8,12, -1, 3, 7,
-3,-4, 0, 4, 3, 7, -1,-2, 2,
-3,-6,-7, 4, 1, 0, -1,-4,-5,
-3, 1, 0, 4, 8, 7, -1, 3, 2,
-3,-4,-5, 4, 3, 2, -1,-2,-3};
void run_test(float[] comp){
float[] out=unquantize();
if(comp!=null){
if(out==null){
System.err.println("_book_unquantize incorrectly returned NULL");
System.exit(1);
}
for(int i=0;i<entries*dim;i++){
if(Math.abs(out[i]-comp[i])>.0001){
System.err.println("disagreement in unquantized and reference data:\nposition "+i+": "+out[i]+" != "+comp[i]);
System.exit(1);
}
}
}
else{
if(out!=null){
System.err.println("_book_unquantize returned a value array:\n correct result should have been NULL");
System.exit(1);
}
}
}
public static void main(String[] arg){
// run the nine dequant tests, and compare to the hand-rolled results
System.err.print("Dequant test 1... ");
test1.run_test(test1_result);
System.err.print("OK\nDequant test 2... ");
test2.run_test(test2_result);
System.err.print("OK\nDequant test 3... ");
test3.run_test(test3_result);
System.err.print("OK\nDequant test 4... ");
test4.run_test(test4_result);
System.err.print("OK\nDequant test 5... ");
test5.run_test(test5_result);
System.err.print("OK\n\n");
}
*/
}