com.github.axet.libvorbis.Jstatic_codebook Maven / Gradle / Ivy
package com.github.axet.libvorbis;
import com.github.axet.libogg.Joggpack_buffer;
/**
This structure encapsulates huffman and VQ style encoding books; it
doesn't do anything specific to either.
valuelist/quantlist are nonNULL (and q_* significant) only if
there's entry->value mapping to be done.
If encode-side mapping must be done (and thus the entry needs to be
hunted), the auxiliary encode pointer will point to a decision
tree. This is true of both VQ and huffman, but is mostly useful
with VQ.
*/
public final class Jstatic_codebook {
/** codebook dimensions (elements per vector) */
int dim = 0;
/** codebook entries */
int entries = 0;
/** codeword lengths in bits */
byte[] lengthlist = null;
/** mapping
* 0 = none
* 1 = implicitly populated values from map column
* 2 = listed arbitrary values */
int maptype = 0;
/* The below does a linear, single monotonic sequence mapping. */
/** packed 32 bit float; quant value 0 maps to minval */
int q_min = 0;
/** packed 32 bit float; val 1 - val 0 == delta */
int q_delta = 0;
/** bits: 0 < quant <= 16 */
private int q_quant = 0;
/** bitflag */
private int q_sequencep = 0;
/** map == 1: (int)(entries^(1/dim)) element column map
* map == 2: list of dim*entries quantized entry vals
*/
private int[] quantlist = null;
private int allocedp = 0;
//
Jstatic_codebook() {
}
public Jstatic_codebook(
final int i_dim,
final int i_entries,
final byte[] pi_lengthlist,
final int i_maptype,
final int i_q_min,
final int i_q_delta,
final int i_q_quant,
final int i_q_sequencep,
final int[] pi_quantlist,
final int i_allocedp )
{
dim = i_dim;
entries = i_entries;
lengthlist = pi_lengthlist;
maptype = i_maptype;
q_min = i_q_min;
q_delta = i_q_delta;
q_quant = i_q_quant;
q_sequencep = i_q_sequencep;
quantlist = pi_quantlist;
allocedp = i_allocedp;
}
private final void clear() {
dim = 0;
entries = 0;
lengthlist = null;
maptype = 0;
q_min = 0;
q_delta = 0;
q_quant = 0;
q_sequencep = 0;
quantlist = null;
allocedp = 0;
}
// sharedbook.c
/** 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 */
final int _book_maptype1_quantvals() {
final int e = this.entries;// java
if( e < 1 ) {
return 0;
}
final int d = this.dim;// java
int vals = (int)Math.floor( Math.pow( (double)e, 1. / d ) );
/* 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 */
if( vals < 1 ) {
vals = 1;
}
while( true ) {
int acc = 1;
int acc1 = 1;
int i;
for( i = 0; i < d; i++ ) {
if( e / vals < acc ) {
break;
}
acc *= vals;
if( Integer.MAX_VALUE / (vals + 1) < acc1 ) {
acc1 = Integer.MAX_VALUE;
} else {
acc1 *= vals + 1;
}
}
if( i >= d && acc <= e && acc1 > e ) {
return (vals);
} else {
if( i < d || acc > e ) {
vals--;
} else {
vals++;
}
}
}
}
/** 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 */
final float[] _book_unquantize(final int n, final int[] sparsemap) {
if( this.maptype == 1 || this.maptype == 2 ) {
final float mindel = Jcodec._float32_unpack( this.q_min );
final float delta = Jcodec._float32_unpack( this.q_delta );
final int dimension = this.dim;// java
final float[] r = new float[n * dimension];
int count = 0;
final boolean is_qsequencep = this.q_sequencep != 0;
/* maptype 1 and 2 both use a quantized value vector, but
different sizes */
final int[] quant_list = this.quantlist;// java
final byte[] length_list = this.lengthlist;// java
switch( this.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 last ) {
for( j = last; j < ithis; j++ ) {
opb.oggpack_write( i - count, Jcodec.ov_ilog( entries_count - count ) );
count = i;
}
}
}
opb.oggpack_write( i - count, Jcodec.ov_ilog( entries_count - 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.oggpack_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_count; i++ ) {
if( length_list[i] == 0 ) {
break;
}
}
if( i == entries_count ) {
opb.oggpack_write( 0, 1 ); /* no unused entries */
for( i = 0; i < entries_count; i++ ) {
opb.oggpack_write( length_list[i] - 1, 5 );
}
} else {
opb.oggpack_write( 1, 1 ); /* we have unused entries; thus we tag */
for( i = 0; i < entries_count; i++ ) {
if( length_list[i] == 0 ) {
opb.oggpack_write( 0, 1 );
} else {
opb.oggpack_write( 1, 1 );
opb.oggpack_write( length_list[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.oggpack_write( this.maptype, 4 );
switch( this.maptype ) {
case 0:
/* no mapping */
break;
case 1: case 2:
/* implicitly populated value mapping */
/* explicitly populated value mapping */
if( this.quantlist == null ) {
/* no quantlist? error */
return (-1);
}
/* values that define the dequantization */
opb.oggpack_write( this.q_min, 32 );
opb.oggpack_write( this.q_delta, 32 );
opb.oggpack_write( this.q_quant - 1, 4 );
opb.oggpack_write( this.q_sequencep, 1 );
{
int quantvals;
switch( this.maptype ) {
case 1:
/* a single column of (c->entries.->dim) quantized values for
building a full value list algorithmically (square lattice) */
quantvals = _book_maptype1_quantvals();
break;
case 2:
/* every value (c->entries*c->dim total) specified explicitly */
quantvals = entries_count * this.dim;
break;
default: /* NOT_REACHABLE */
quantvals = -1;
}
/* quantized values */
final int[] quant_list = this.quantlist;// java
for( i = 0; i < quantvals; i++ ) {
int val = quant_list[i];
if( val < 0 ) {
val = -val;
}
opb.oggpack_write( val, this.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 */
static final Jstatic_codebook vorbis_staticbook_unpack(final Joggpack_buffer opb) {
final Jstatic_codebook s = new Jstatic_codebook();
s.allocedp = 1;
/* make sure alignment is correct */
if( opb.oggpack_read( 24 ) != 0x564342 ) {// goto _eofout;
s.vorbis_staticbook_destroy();
return (null);
}
/* first the basic parameters */
s.dim = opb.oggpack_read( 16 );
final int entries = opb.oggpack_read( 24 );
s.entries = entries;
if( entries == -1 ) {// goto _eofout;
s.vorbis_staticbook_destroy();
return (null);
}
if( Jcodec.ov_ilog( s.dim ) + Jcodec.ov_ilog( entries ) > 24 ) {//goto _eofout;
s.vorbis_staticbook_destroy();
return (null);
}
/* codeword ordering.... length ordered or unordered? */
switch( opb.oggpack_read( 1 ) ) {
case 0: {
int unused;
/* allocated but unused entries? */
unused = opb.oggpack_read( 1 );
if( (entries * (unused != 0 ? 1 : 5) + 7) >> 3
>
opb.storage - opb.oggpack_bytes() ) {// goto _eofout;
s.vorbis_staticbook_destroy();
return (null);
}
/* unordered */
final byte[] lengthlist = new byte[entries];
s.lengthlist = lengthlist;
/* allocated but unused entries? */
if( unused != 0 ) {
/* yes, unused entries */
for( int i = 0; i < entries; i++ ) {
if( opb.oggpack_read( 1 ) != 0 ) {
final int num = opb.oggpack_read( 5 );
if( num == -1 ) {// goto _eofout;
s.vorbis_staticbook_destroy();
return (null);
}
lengthlist[i] = (byte)(num + 1);
} else {
lengthlist[i] = 0;
}
}
} else {
/* all entries used; no tagging */
for( int i = 0; i < entries; i++ ) {
final int num = opb.oggpack_read( 5 );
if( num == -1 ) {// goto _eofout;
s.vorbis_staticbook_destroy();
return (null);
}
lengthlist[i] = (byte)(num + 1);
}
}
break;
}
case 1:
/* ordered */
{
int length = opb.oggpack_read( 5 ) + 1;
if( length == 0 ) {// goto _eofout;
s.vorbis_staticbook_destroy();
return (null);
}
final byte[] lengthlist = new byte[entries];
s.lengthlist = lengthlist;
for( int i = 0; i < entries; ) {
final int num = opb.oggpack_read( Jcodec.ov_ilog( entries - i ) );
if( num == -1 ) {// goto _eofout;
s.vorbis_staticbook_destroy();
return (null);
}
if( length > 32 || num > entries - i ||
(num > 0 && (num - 1) >> (length - 1 ) > 1 ) ) {
s.vorbis_staticbook_destroy();
return (null);//goto _errout;
}
if( length > 32 ) {// goto _errout;
s.vorbis_staticbook_destroy();
return (null);
}
for( int j = 0; j < num; j++, i++ ) {
lengthlist[i] = (byte)length;
}
length++;
}
}
break;
default:
/* EOF */
// goto _eofout;
s.vorbis_staticbook_destroy();
return (null);
}
/* Do we have a mapping to unpack? */
switch( (s.maptype = opb.oggpack_read( 4 )) ) {
case 0:
/* no mapping */
break;
case 1: case 2:
/* implicitly populated value mapping */
/* explicitly populated value mapping */
s.q_min = opb.oggpack_read( 32 );
s.q_delta = opb.oggpack_read( 32 );
final int q_quant = opb.oggpack_read( 4 ) + 1;
s.q_quant = q_quant;
s.q_sequencep = opb.oggpack_read( 1 );
if( s.q_sequencep == -1 ) {// goto _eofout;
s.vorbis_staticbook_destroy();
return (null);
}
{
int quantvals = 0;
switch( s.maptype ) {
case 1:
quantvals = (s.dim == 0 ? 0 : s._book_maptype1_quantvals() );
break;
case 2:
quantvals = entries * s.dim;
break;
}
/* quantized values */
if( ((quantvals * q_quant + 7) >> 3) > opb.storage - opb.oggpack_bytes() ) {
s.vorbis_staticbook_destroy();
return (null);// goto _eofout;
}
final int[] quantlist = new int[quantvals];
s.quantlist = quantlist;
for( int i = 0; i < quantvals; i++ ) {
quantlist[i] = opb.oggpack_read( q_quant );
}
if( quantvals != 0 && quantlist[quantvals - 1] == -1 ) {//goto _eofout;
s.vorbis_staticbook_destroy();
return (null);
}
}
break;
default:
//goto _errout;
s.vorbis_staticbook_destroy();
return (null);
}
/* all set */
return (s);
//_errout:
//_eofout:
// vorbis_staticbook_destroy( s );
// return (null);
}
}