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