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