/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ /* 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.Buffer; 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 StaticCodeBook() { } 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, Util.ilog(entries - count)); count = i; } } } opb.write(i - count, Util.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(Util.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() { } // 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]; // 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); } // 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 exp = (val & 0x7fe00000) >>> VQ_FMAN; if ((val & 0x80000000) != 0) 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)); } }