/*
* THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE.
* USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS
* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE
* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.
*
* THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002
* by the Xiph.Org Foundation http://www.xiph.org/
*/
package org.xiph.libvorbis;
import java.util.*;
import static org.xiph.libvorbis.vorbis_constants.integer_constants.*;
class oggpack_buffer {
int endbyte; // long
int endbit;
byte[] buffer; // unsigned char *buffer;
int ptr; // unsigned char *ptr; // offset
int storage; // long
static int[] mask = new int[]{
0x00000000,
0x00000001,
0x00000003,
0x00000007,
0x0000000f,
0x0000001f,
0x0000003f,
0x0000007f,
0x000000ff,
0x000001ff,
0x000003ff,
0x000007ff,
0x00000fff,
0x00001fff,
0x00003fff,
0x00007fff,
0x0000ffff,
0x0001ffff,
0x0003ffff,
0x0007ffff,
0x000fffff,
0x001fffff,
0x003fffff,
0x007fffff,
0x00ffffff,
0x01ffffff,
0x03ffffff,
0x07ffffff,
0x0fffffff,
0x1fffffff,
0x3fffffff,
0x7fffffff,
0xffffffff};
public oggpack_buffer() { // void oggpack_writeinit(oggpack_buffer *b)
ptr = 0;
buffer = new byte[BUFFER_INCREMENT];
buffer[0] = (byte) '\0';
storage = BUFFER_INCREMENT;
}
public void oggpack_reset() {
ptr = 0;
buffer[0] = (byte) '\0';
endbit = 0;
endbyte = 0;
}
public int oggpack_bytes() {
return (endbyte + (endbit + 7) / 8);
}
public int oggpack_bits() {
return (endbyte * 8 + endbit);
}
public void oggpack_write(int value8, int bits) {
if (endbyte + 4 >= storage) {
storage += BUFFER_INCREMENT;
// b->buffer=_ogg_realloc(b->buffer,b->storage+BUFFER_INCREMENT);
byte[] temp = new byte[storage];
System.arraycopy(buffer, 0, temp, 0, buffer.length);
buffer = temp;
ptr = endbyte;
}
value8 &= mask[bits];
bits += endbit;
buffer[ptr] |= value8 << endbit;
if (bits >= 8) {
buffer[ptr + 1] = (byte) (value8 >>> (8 - endbit));
if (bits >= 16) {
buffer[ptr + 2] = (byte) (value8 >>> (16 - endbit));
if (bits >= 24) {
buffer[ptr + 3] = (byte) (value8 >>> (24 - endbit));
if (bits >= 32) {
if (endbit != 0) {
buffer[ptr + 4] = (byte) (value8 >>> (32 - endbit));
} else {
buffer[ptr + 4] = 0;
}
}
}
}
}
endbyte += bits / 8;
ptr += bits / 8;
endbit = bits & 7;
}
public void oggpack_writetrunc(int bits) {
int bytes = bits >> 3;
bits -= bytes * 8;
// b->ptr=b->buffer+bytes;
ptr = bytes;
endbit = bits;
endbyte = bytes;
// *b->ptr&=mask[bits];
buffer[ptr] = (byte) mask[bits];
}
public void _v_writestring(String s, int bytes) {
for (int i = 0; i < bytes; i++) {
oggpack_write(s.charAt(i), 8);
}
}
public void _v_writestring(byte[] s, int bytes) {
for (int i = 0; i < bytes; i++) {
oggpack_write(s[i], 8);
}
}
public boolean _vorbis_pack_info(vorbis_info vi) {
codec_setup_info ci = vi.codec_setup;
if (ci == null)
return false;
// preamble
oggpack_write(0x01, 8);
_v_writestring("vorbis", 6);
// basic information about the stream
oggpack_write(0x00, 32);
oggpack_write(vi.channels, 8);
oggpack_write(vi.rate, 32);
oggpack_write(vi.bitrate_upper, 32);
oggpack_write(vi.bitrate_nominal, 32);
oggpack_write(vi.bitrate_lower, 32);
oggpack_write(ilog2(ci.blocksizes[0]), 4);
oggpack_write(ilog2(ci.blocksizes[1]), 4);
oggpack_write(1, 1);
return true;
}
public boolean _vorbis_pack_comment(vorbis_comment vc) {
String temp = "Xiph.Org libVorbis I 20030909";
int bytes = temp.length();
// preamble
oggpack_write(0x03, 8);
_v_writestring("vorbis", 6);
// vendor
oggpack_write(bytes, 32);
_v_writestring(temp, bytes);
// comments
oggpack_write(vc.comments, 32);
if (vc.comments > 0) {
for (int i = 0; i < vc.comments; i++) {
if (vc.user_comments[i] != null) {
oggpack_write(vc.comment_lengths[i], 32);
_v_writestring(vc.user_comments[i], vc.comment_lengths[i]);
} else {
oggpack_write(0, 32);
}
}
}
oggpack_write(1, 1);
return true;
}
public boolean _vorbis_pack_books(vorbis_info vi) {
codec_setup_info ci = vi.codec_setup;
int i;
if (ci == null)
return false;
// preamble
oggpack_write(0x05, 8);
_v_writestring("vorbis", 6);
// books
oggpack_write(ci.books - 1, 8);
for (i = 0; i < ci.books; i++) {
if (!vorbis_staticbook_pack(ci.book_param[i]))
return false;
}
// times; hook placeholders
oggpack_write(0, 6);
oggpack_write(0, 16);
// floors
oggpack_write(ci.floors - 1, 6);
for (i = 0; i < ci.floors; i++) {
oggpack_write(ci.floor_type[i], 16);
// _floor_P[ci->floor_type[i]]->pack(ci->floor_param[i],opb);
floor1_pack(ci.floor_param[i]);
}
// residues
oggpack_write(ci.residues - 1, 6);
for (i = 0; i < ci.residues; i++) {
oggpack_write(ci.residue_type[i], 16);
// _residue_P[ci->residue_type[i]]->pack(ci->residue_param[i],opb);
res0_pack(ci.residue_param[i]);
}
// maps
oggpack_write(ci.maps - 1, 6);
for (i = 0; i < ci.maps; i++) {
oggpack_write(ci.map_type[i], 16);
// _mapping_P[ci->map_type[i]]->pack(vi,ci->map_param[i],opb);
mapping0_pack(vi, ci.map_param[i]);
}
// modes
oggpack_write(ci.modes - 1, 6);
for (i = 0; i < ci.modes; i++) {
oggpack_write(ci.mode_param[i].blockflag, 1);
oggpack_write(ci.mode_param[i].windowtype, 16);
oggpack_write(ci.mode_param[i].transformtype, 16);
oggpack_write(ci.mode_param[i].mapping, 8);
}
oggpack_write(1, 1);
return true;
}
public boolean vorbis_staticbook_pack(static_codebook c) {
int i, j;
int ordered = 0;
// first the basic parameters
oggpack_write(0x564342, 24);
oggpack_write(c.dim, 16);
oggpack_write(c.entries, 24);
// pack the codewords. There are two packings; length ordered and length random. Decide between the two now.
for (i = 1; i < c.entries; i++)
if (c.lengthlist[i - 1] == 0 || c.lengthlist[i] < c.lengthlist[i - 1])
break;
if (i == c.entries)
ordered = 1;
if (ordered != 0) {
// length ordered. We only need to say how many codewords of each length.
// The actual codewords are generated deterministically
int count = 0;
oggpack_write(1, 1); // ordered
oggpack_write(c.lengthlist[0] - 1, 5); // 1 to 32
for (i = 1; i < c.entries; i++) {
int current = c.lengthlist[i];
int last = c.lengthlist[i - 1];
if (current > last) {
for (j = last; j < current; j++) {
oggpack_write(i - count, ilog(c.entries - count));
count = i;
}
}
}
oggpack_write(i - count, ilog(c.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
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 < c.entries; i++)
if (c.lengthlist[i] == 0)
break;
if (i == c.entries) {
oggpack_write(0, 1); // no unused entries
for (i = 0; i < c.entries; i++)
oggpack_write(c.lengthlist[i] - 1, 5);
} else {
oggpack_write(1, 1); // we have unused entries; thus we tag
for (i = 0; i < c.entries; i++) {
if (c.lengthlist[i] == 0) {
oggpack_write(0, 1);
} else {
oggpack_write(1, 1);
oggpack_write(c.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?
oggpack_write(c.maptype, 4);
switch (c.maptype) {
case 0:
// no mapping
break;
case 1:
case 2:
// implicitly populated value mapping
// explicitly populated value mapping
if (c.quantlist == null) {
// no quantlist? error
System.out.println("no quantlist exists");
return false;
}
// values that define the dequantization
oggpack_write(c.q_min, 32);
oggpack_write(c.q_delta, 32);
oggpack_write(c.q_quant - 1, 4);
oggpack_write(c.q_sequencep, 1);
{
int quantvals;
switch (c.maptype) {
case 1:
// a single column of (c->entries/c->dim) quantized values for
// building a full value list algorithmically (square lattice)
quantvals = c._book_maptype1_quantvals();
break;
case 2:
// every value (c->entries*c->dim total) specified explicitly
quantvals = c.entries * c.dim;
break;
default: // NOT_REACHABLE
quantvals = -1;
}
// quantized values
for (i = 0; i < quantvals; i++)
oggpack_write(Math.abs(c.quantlist[i]), c.q_quant);
}
break;
default:
// error case; we don't have any other map types now
System.out.println("error case; we don't have any other map types now");
return false;
}
return true;
}
public int vorbis_book_encode(codebook book, int a) {
oggpack_write(book.codelist[a], book.c.lengthlist[a]);
return book.c.lengthlist[a];
}
public void floor1_pack(vorbis_info_floor1 info) {
int j, k;
int count = 0;
int rangebits;
int maxposit = info.postlist[1];
int maxclass = -1;
// save out partitions
oggpack_write(info.partitions, 5); // only 0 to 31 legal
for (j = 0; j < info.partitions; j++) {
oggpack_write(info.partitionclass[j], 4); // only 0 to 15 legal
if (maxclass < info.partitionclass[j])
maxclass = info.partitionclass[j];
}
// save out partition classes
for (j = 0; j < maxclass + 1; j++) {
oggpack_write(info.class_dim[j] - 1, 3); // 1 to 8
oggpack_write(info.class_subs[j], 2); // 0 to 3
if (info.class_subs[j] > 0)
oggpack_write(info.class_book[j], 8);
for (k = 0; k < (1 << info.class_subs[j]); k++)
oggpack_write(info.class_subbook[j][k] + 1, 8);
}
// save out the post list
oggpack_write(info.mult - 1, 2); // only 1,2,3,4 legal now
oggpack_write(ilog2(maxposit), 4);
rangebits = ilog2(maxposit);
for (j = 0, k = 0; j < info.partitions; j++) {
count += info.class_dim[info.partitionclass[j]];
for (; k < count; k++)
oggpack_write(info.postlist[k + 2], rangebits);
}
}
static int seq = 0;
public int floor1_encode(vorbis_block vb, vorbis_look_floor1 look, int[] post, int[] ilogmask) {
int i, j;
vorbis_info_floor1 info = look.vi;
// int n = look.n;
int posts = look.posts;
codec_setup_info ci = vb.vd.vi.codec_setup;
int[] out = new int[VIF_POSIT + 2];
static_codebook[] sbooks = ci.book_param;
codebook[] books = ci.fullbooks;
// quantize values to multiplier spec
if (post != null) {
for (i = 0; i < posts; i++) {
int val = post[i] & 0x7fff;
switch (info.mult) {
case 1: /* 1024 -> 256 */
val >>= 2;
break;
case 2: /* 1024 -> 128 */
val >>= 3;
break;
case 3: /* 1024 -> 86 */
val /= 12;
break;
case 4: /* 1024 -> 64 */
val >>= 4;
break;
}
post[i] = val | (post[i] & 0x8000);
}
out[0] = post[0];
out[1] = post[1];
// find prediction values for each post and subtract them
for (i = 2; i < posts; i++) {
int ln = look.loneighbor[i - 2];
int hn = look.hineighbor[i - 2];
int x0 = info.postlist[ln];
int x1 = info.postlist[hn];
int y0 = post[ln];
int y1 = post[hn];
int predicted = render_point(x0, x1, y0, y1, info.postlist[i]);
if (((post[i] & 0x8000) > 0) || (predicted == post[i])) {
post[i] = predicted | 0x8000; // in case there was roundoff jitter in interpolation
out[i] = 0;
} else {
int headroom = (look.quant_q - predicted < predicted ? look.quant_q - predicted : predicted);
int val = post[i] - predicted;
// at this point the 'deviation' value is in the range +/- max
// range, but the real, unique range can always be mapped to
// only [0-maxrange). So we want to wrap the deviation into
// this limited range, but do it in the way that least screws
// an essentially gaussian probability distribution.
if (val < 0)
if (val < -headroom)
val = headroom - val - 1;
else
val = -1 - (val << 1);
else if (val >= headroom)
val = val + headroom;
else
val <<= 1;
out[i] = val;
post[ln] &= 0x7fff;
post[hn] &= 0x7fff;
}
}
// we have everything we need. pack it out
// mark nontrivial floor
oggpack_write(1, 1);
// beginning/end post
look.frames++;
look.postbits += ilog(look.quant_q - 1) * 2;
oggpack_write(out[0], ilog(look.quant_q - 1));
oggpack_write(out[1], ilog(look.quant_q - 1));
// partition by partition
for (i = 0, j = 2; i < info.partitions; i++) {
int class_local = info.partitionclass[i];
int cdim = info.class_dim[class_local];
int csubbits = info.class_subs[class_local];
int csub = 1 << csubbits;
// int bookas[8]={0,0,0,0,0,0,0,0};
int[] bookas = new int[8];
int cval = 0;
int cshift = 0;
int k, l;
// generate the partition's first stage cascade value
if (csubbits > 0) {
// int maxval[8];
int[] maxval = new int[8];
for (k = 0; k < csub; k++) {
int booknum = info.class_subbook[class_local][k];
if (booknum < 0) {
maxval[k] = 1;
} else {
maxval[k] = sbooks[info.class_subbook[class_local][k]].entries;
}
}
for (k = 0; k < cdim; k++) {
for (l = 0; l < csub; l++) {
int val = out[j + k];
if (val < maxval[l]) {
bookas[k] = l;
break;
}
}
cval |= bookas[k] << cshift;
cshift += csubbits;
}
// write it
look.phrasebits += vorbis_book_encode(books[info.class_book[class_local]], cval);
}
// write post values
for (k = 0; k < cdim; k++) {
int book = info.class_subbook[class_local][bookas[k]];
if (book >= 0) {
// hack to allow training with 'bad' books
if (out[j + k] < books[book].entries)
look.postbits += vorbis_book_encode(books[book], out[j + k]);
/*else
* fprintf(stderr,"+!");*/
}
}
j += cdim;
}
// generate quantized floor equivalent to what we'd unpack in decode
// render the lines
int hx = 0;
int lx = 0;
int ly = post[0] * info.mult;
for (j = 1; j < look.posts; j++) {
int current = look.forward_index[j];
int hy = post[current] & 0x7fff;
if (hy == post[current]) {
hy *= info.mult;
hx = info.postlist[current];
render_line0(lx, hx, ly, hy, ilogmask);
lx = hx;
ly = hy;
}
}
for (j = hx; j < vb.pcmend / 2; j++)
ilogmask[j] = ly; // be certain
seq++;
return (1);
} else {
oggpack_write(0, 1);
// memset(ilogmask,0,vb.pcmend/2*sizeof(*ilogmask));
// for ( i=0; i < vb.pcmend/2; i++ )
// ilogmask[i] = 0;
Arrays.fill(ilogmask, 0, vb.pcmend / 2, 0);
seq++;
return (0);
}
}
public void res0_pack(vorbis_info_residue0 info) {
int j, acc = 0;
oggpack_write(info.begin, 24);
oggpack_write(info.end, 24);
oggpack_write(info.grouping - 1, 24); // residue vectors to group and code with a partitioned book
oggpack_write(info.partitions - 1, 6); // possible partition choices
oggpack_write(info.groupbook, 8); // group huffman book
// secondstages is a bitmask; as encoding progresses pass by pass, a bitmask
// of one indicates this partition class has bits to write this pass
for (j = 0; j < info.partitions; j++) {
if (ilog(info.secondstages[j]) > 3) {
// yes, this is a minor hack due to not thinking ahead
oggpack_write(info.secondstages[j], 3);
oggpack_write(1, 1);
oggpack_write(info.secondstages[j] >> 3, 5);
} else {
oggpack_write(info.secondstages[j], 4); // trailing zero
}
acc += icount(info.secondstages[j]);
}
for (j = 0; j < acc; j++)
oggpack_write(info.booklist[j], 8);
}
public void mapping0_pack(vorbis_info vi, vorbis_info_mapping0 info) {
int i;
/* 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) {
oggpack_write(1, 1);
oggpack_write(info.submaps - 1, 4);
} else
oggpack_write(0, 1);
if (info.coupling_steps > 0) {
oggpack_write(1, 1);
oggpack_write(info.coupling_steps - 1, 8);
for (i = 0; i < info.coupling_steps; i++) {
oggpack_write(info.coupling_mag[i], ilog2(vi.channels));
oggpack_write(info.coupling_ang[i], ilog2(vi.channels));
}
} else
oggpack_write(0, 1);
oggpack_write(0, 2); // 2,3:reserved
// we don't write the channel submappings if we only have one...
if (info.submaps > 1) {
for (i = 0; i < vi.channels; i++)
oggpack_write(info.chmuxlist[i], 4);
}
for (i = 0; i < info.submaps; i++) {
oggpack_write(0, 8); // time submap unused
oggpack_write(info.floorsubmap[i], 8);
oggpack_write(info.residuesubmap[i], 8);
}
}
}