package org.kc7bfi.jflac.io; /** * libFLAC - Free Lossless Audio Codec library Copyright (C) 2000,2001,2002,2003 * Josh Coalson * * This library 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 library 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 library; if not, write to the Free Software Foundation, Inc., * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ import org.kc7bfi.jflac.util.CRC16; import org.kc7bfi.jflac.util.CRC8; import java.io.IOException; import java.io.OutputStream; /** * Bit-wide output stream. * * @author kc7bfi */ public class BitOutputStream { private static final int BITS_PER_BLURB = 8; //private static final int BITS_PER_BLURB_LOG2 = 3; //private static final int BYTES_PER_BLURB = 1; //private static final byte BLURB_TOP_BIT_ONE = ((byte) 0x80); private static final long[] MASK32 = new long[]{0, 0x0000000000000001, 0x0000000000000003, 0x0000000000000007, 0x000000000000000F, 0x000000000000001F, 0x000000000000003F, 0x000000000000007F, 0x00000000000000FF, 0x00000000000001FF, 0x00000000000003FF, 0x00000000000007FF, 0x0000000000000FFF, 0x0000000000001FFF, 0x0000000000003FFF, 0x0000000000007FFF, 0x000000000000FFFF, 0x000000000001FFFF, 0x000000000003FFFF, 0x000000000007FFFF, 0x00000000000FFFFF, 0x00000000001FFFFF, 0x00000000003FFFFF, 0x00000000007FFFFF, 0x0000000000FFFFFF, 0x0000000001FFFFFF, 0x0000000003FFFFFF, 0x0000000007FFFFFF, 0x000000000FFFFFFF, 0x000000001FFFFFFF, 0x000000003FFFFFFF, 0x000000007FFFFFFF, 0x00000000FFFFFFFF, 0x00000001FFFFFFFFL, 0x00000003FFFFFFFFL, 0x00000007FFFFFFFFL, 0x0000000FFFFFFFFFL, 0x0000001FFFFFFFFFL, 0x0000003FFFFFFFFFL, 0x0000007FFFFFFFFFL, 0x000000FFFFFFFFFFL, 0x000001FFFFFFFFFFL, 0x000003FFFFFFFFFFL, 0x000007FFFFFFFFFFL, 0x00000FFFFFFFFFFFL, 0x00001FFFFFFFFFFFL, 0x00003FFFFFFFFFFFL, 0x00007FFFFFFFFFFFL, 0x0000FFFFFFFFFFFFL, 0x0001FFFFFFFFFFFFL, 0x0003FFFFFFFFFFFFL, 0x0007FFFFFFFFFFFFL, 0x000FFFFFFFFFFFFFL, 0x001FFFFFFFFFFFFFL, 0x003FFFFFFFFFFFFFL, 0x007FFFFFFFFFFFFFL, 0x00FFFFFFFFFFFFFFL, 0x01FFFFFFFFFFFFFFL, 0x03FFFFFFFFFFFFFFL, 0x07FFFFFFFFFFFFFFL, 0x0FFFFFFFFFFFFFFFL, 0x1FFFFFFFFFFFFFFFL, 0x3FFFFFFFFFFFFFFFL, 0x7FFFFFFFFFFFFFFFL, 0xFFFFFFFFFFFFFFFFL}; private byte[] buffer = new byte[0]; private int outCapacity = 0; // in blurbs private int outBlurbs = 0; private int outBits = 0; private int totalBits = 0; // must always == BITS_PER_BLURB*blurbs+bits private int consumedBlurbs = 0; private int consumedBits = 0; private int totalConsumedBits = 0; private short readCRC16 = 0; private OutputStream os; /* * WATCHOUT: The current implentation is not friendly to shrinking, i.e. it * does not shift left what is consumed, it just chops off the end, whether * there is unconsumed data there or not. This is OK because currently we * never shrink the buffer, but if this ever changes, we'll have to do some * fixups here. */ private boolean resize(int newCapacity) { if (outCapacity >= newCapacity) return true; byte[] newBuffer = new byte[newCapacity]; System.arraycopy(buffer, 0, newBuffer, 0, Math.min(outBlurbs + ((outBits != 0) ? 1 : 0), newCapacity)); if (newCapacity < outBlurbs + ((outBits != 0) ? 1 : 0)) { outBlurbs = newCapacity; outBits = 0; totalBits = newCapacity << 3; } if (newCapacity < consumedBlurbs + ((consumedBits != 0) ? 1 : 0)) { consumedBlurbs = newCapacity; consumedBits = 0; totalConsumedBits = newCapacity << 3; } buffer = newBuffer; outCapacity = newCapacity; return true; } private boolean grow(int minBlurbsToAdd) { int newCapacity = Math.max(outCapacity * 2, outCapacity + minBlurbsToAdd); return resize(newCapacity); } private boolean ensureSize(int bitsToAdd) { if ((outCapacity << 3) < totalBits + bitsToAdd) { return grow((bitsToAdd >> 3) + 2); } return true; } /* private int readFromStream() throws IOException { // first shift the unconsumed buffer data toward the front as much as possible if (totalConsumedBits >= BITS_PER_BLURB) { int l = 0; int r = consumedBlurbs; int r_end = inBlurbs + ((inBits != 0) ? 1 : 0); for (; r < r_end; l++, r++) buffer[l] = buffer[r]; for (; l < r_end; l++) buffer[l] = 0; inBlurbs -= consumedBlurbs; totalBits -= consumedBlurbs << 3; consumedBlurbs = 0; totalConsumedBits = consumedBits; } // grow if we need to if (capacity <= 1) resize(16); // set the target for reading, taking into account blurb alignment // blurb == byte, so no gyrations necessary: int bytes = capacity - inBlurbs; // finally, read in some data bytes = is.read(buffer, inBlurbs, bytes); if (bytes <= 0) throw new EOFException(); // now we have to handle partial blurb cases: // blurb == byte, so no gyrations necessary: inBlurbs += bytes; totalBits += bytes << 3; return bytes; } */ /** * The constructor. * * @param os The InputStream to read bits from */ public BitOutputStream(OutputStream os) { this.os = os; } /** * The constructor. */ public BitOutputStream() { } /** TODO */ //public void clear() { // //} /** * Concatinate one InputBitStream to the end of this one. * * @param src The inputBitStream to copy * @return True if copy was successful */ public boolean concatenateAligned(BitOutputStream src) { int bitsToAdd = src.totalBits - src.totalConsumedBits; if (bitsToAdd == 0) return true; if (outBits != src.consumedBits) return false; if (!ensureSize(bitsToAdd)) return false; if (outBits == 0) { System.arraycopy(src.buffer, src.consumedBlurbs, buffer, outBlurbs, (src.outBlurbs - src.consumedBlurbs + ((src.outBits != 0) ? 1 : 0))); } else if (outBits + bitsToAdd > BITS_PER_BLURB) { buffer[outBlurbs] <<= (BITS_PER_BLURB - outBits); buffer[outBlurbs] |= (src.buffer[src.consumedBlurbs] & ((1 << (BITS_PER_BLURB - outBits)) - 1)); System.arraycopy(src.buffer, src.consumedBlurbs + 1, buffer, outBlurbs + 11, (src.outBlurbs - src.consumedBlurbs - 1 + ((src.outBits != 0) ? 1 : 0))); } else { buffer[outBlurbs] <<= bitsToAdd; buffer[outBlurbs] |= (src.buffer[src.consumedBlurbs] & ((1 << bitsToAdd) - 1)); } outBits = src.outBits; totalBits += bitsToAdd; outBlurbs = totalBits / BITS_PER_BLURB; return true; } /** * Reset the read CRC-16 value. * * @param seed The initial CRC-16 value */ public void resetReadCRC16(short seed) { readCRC16 = seed; } /** * return the read CRC-16 value. * * @return The read CRC-16 value */ public short getReadCRC16() { return readCRC16; } /** * return the write CRC-16 value. * * @return The write CRC-16 value */ public short getWriteCRC16() { return CRC16.calc(buffer, outBlurbs); } /** * return the write CRC-8 value. * * @return The write CRC-8 value */ public byte getWriteCRC8() { return CRC8.calc(buffer, outBlurbs); } /** * Test if the Bit Stream is byte aligned. * * @return True of bit stream is byte aligned */ public boolean isByteAligned() { return ((outBits & 7) == 0); } /** * Test if the Bit Stream consumed bits is byte aligned. * * @return True of bit stream consumed bits is byte aligned */ public boolean isConsumedByteAligned() { return ((consumedBits & 7) == 0); } /** * return the number of bits to read to align the byte. * * @return The number of bits to align the byte */ public int bitsLeftForByteAlignment() { return 8 - (consumedBits & 7); } /** * return the number of bytes left to read. * * @return The number of bytes left to read */ public int getInputBytesUnconsumed() { return (totalBits - totalConsumedBits) >> 3; } /** * Write zero bits. * * @param bits The number of zero bits to write * @throws IOException On write error */ public void writeZeroes(int bits) throws IOException { if (bits == 0) return; if (!ensureSize(bits)) throw new IOException("Memory Allocation Error"); totalBits += bits; while (bits > 0) { int n = Math.min(BITS_PER_BLURB - outBits, bits); buffer[outBlurbs] <<= n; bits -= n; outBits += n; if (outBits == BITS_PER_BLURB) { outBlurbs++; outBits = 0; } } } /** * Write a true/false integer. * * @param val The true/false value * @param bits The bit size to write * @throws IOException On write error */ public void writeRawUInt(boolean val, int bits) throws IOException { writeRawUInt((val) ? 1 : 0, bits); } public void writeRawUInt(int val, int bits) throws IOException { if (bits == 0) return; // inline the size check so we don't incure a function call unnecessarily if ((outCapacity << 3) < totalBits + bits) { if (!ensureSize(bits)) throw new IOException("Memory allocation error"); } // zero-out unused bits; WATCHOUT: other code relies on this, so this needs to stay if (bits < 32) val &= (~(0xffffffff << bits)); // zero-out unused bits totalBits += bits; while (bits > 0) { int n = BITS_PER_BLURB - outBits; if (n == BITS_PER_BLURB) { // i.e. outBits == 0 if (bits < BITS_PER_BLURB) { buffer[outBlurbs] = (byte) val; outBits = bits; break; } else if (bits == BITS_PER_BLURB) { buffer[outBlurbs++] = (byte) val; break; } else { int k = bits - BITS_PER_BLURB; buffer[outBlurbs++] = (byte) (val >> k); // we know k < 32 so no need to protect against the gcc bug mentioned above val &= (~(0xffffffff << k)); bits -= BITS_PER_BLURB; } } else if (bits <= n) { buffer[outBlurbs] <<= bits; buffer[outBlurbs] |= val; if (bits == n) { outBlurbs++; outBits = 0; } else outBits += bits; break; } else { int k = bits - n; buffer[outBlurbs] <<= n; buffer[outBlurbs] |= (val >> k); // we know n > 0 so k < 32 so no need to protect against the gcc bug mentioned above val &= (~(0xffffffff << k)); bits -= n; outBlurbs++; outBits = 0; } } } public void writeRawInt(int val, int bits) throws IOException { writeRawUInt((int) val, bits); } public void writeRawULong(long val, int bits) throws IOException { if (bits == 0) return; if (!ensureSize(bits)) throw new IOException("Memory Allocate Error"); val &= MASK32[bits]; totalBits += bits; while (bits > 0) { if (outBits == 0) { if (bits < BITS_PER_BLURB) { buffer[outBlurbs] = (byte) val; outBits = bits; break; } else if (bits == BITS_PER_BLURB) { buffer[outBlurbs++] = (byte) val; break; } else { int k = bits - BITS_PER_BLURB; buffer[outBlurbs++] = (byte) (val >> k); // we know k < 64 so no need to protect against the gcc bug mentioned above val &= (~(0xffffffffffffffffL << k)); bits -= BITS_PER_BLURB; } } else { int n = Math.min(BITS_PER_BLURB - outBits, bits); int k = bits - n; buffer[outBlurbs] <<= n; buffer[outBlurbs] |= (val >> k); // we know n > 0 so k < 64 so no need to protect against the gcc bug mentioned above val &= (~(0xffffffffffffffffL << k)); bits -= n; outBits += n; if (outBits == BITS_PER_BLURB) { outBlurbs++; outBits = 0; } } } } public void writeRawUIntLittleEndian(int val) throws IOException { // NOTE: we rely on the fact that write_raw_uint32() masks out the unused bits writeRawUInt(val, 8); writeRawUInt(val >> 8, 8); writeRawUInt(val >> 16, 8); writeRawUInt(val >> 24, 8); } public void writeByteBlock(byte[] vals, int nvals) throws IOException { // this could be faster but currently we don't need it to be for (int i = 0; i < nvals; i++) { writeRawUInt((int) (vals[i]), 8); } } public void writeUnaryUnsigned(int val) throws IOException { if (val < 32) writeRawUInt(1, ++val); else if (val < 64) writeRawULong(1, ++val); else { writeZeroes(val); writeRawUInt(1, 1); } } public int riceBits(int val, int parameter) { int msbs, uval; // fold signed to unsigned if (val < 0) { // equivalent to (unsigned)(((--val) < < 1) - 1); but without the overflow problem at MININT uval = (int) (((-(++val)) << 1) + 1); } else { uval = (int) (val << 1); } msbs = uval >> parameter; return 1 + parameter + msbs; } /* * DRR FIX # ifdef SYMMETRIC_RICE boolean * write_symmetric_rice_signed(BitBuffer8 * bb, int val, unsigned parameter) { * unsigned total_bits, interesting_bits, msbs; uint32 pattern; * * ASSERT(0 != bb); ASSERT(0 != buffer); ASSERT(parameter <= 31); // init * pattern with the unary end bit and the sign bit if (val < 0) { pattern = * 3; val = -val; } else pattern = 2; * * msbs = val >> parameter; interesting_bits = 2 + parameter; total_bits = * interesting_bits + msbs; pattern < <= parameter; pattern |= (val & ((1 < < * parameter) - 1)); // the binary LSBs * * if (total_bits <= 32) { if (!write_raw_uint32(bb, pattern, total_bits)) * return false; } else { // write the unary MSBs if (!write_zeroes(bb, * msbs)) return false; // write the unary end bit, the sign bit, and binary * LSBs if (!write_raw_uint32(bb, pattern, interesting_bits)) return false; } * return true; } * * boolean write_symmetric_rice_signed_escape(BitBuffer8 * bb, int val, * unsigned parameter) { unsigned total_bits, val_bits; uint32 pattern; * * ASSERT(0 != bb); ASSERT(0 != buffer); ASSERT(parameter <= 31); * * val_bits = bitmath_silog2(val); total_bits = 2 + parameter + 5 + * val_bits; * * if (total_bits <= 32) { pattern = 3; pattern < <= (parameter + 5); * pattern |= val_bits; pattern < <= val_bits; pattern |= (val & ((1 < < * val_bits) - 1)); if (!write_raw_uint32(bb, pattern, total_bits)) return * false; } else { // write the '-0' escape code first if * (!write_raw_uint32(bb, 3 u < < parameter, 2 + parameter)) return false; // * write the length if (!write_raw_uint32(bb, val_bits, 5)) return false; // * write the value if (!write_raw_int32(bb, val, val_bits)) return false; } * return true; } # endif // ifdef SYMMETRIC_RICE */ public void writeRiceSigned(int val, int parameter) throws IOException { int totalBits; int interestingBits; int msbs; int uval; int pattern; // fold signed to unsigned if (val < 0) { // equivalent to (unsigned)(((--val) < < 1) - 1); but without the overflow problem at MININT uval = (int) (((-(++val)) << 1) + 1); } else { uval = (int) (val << 1); } msbs = uval >> parameter; interestingBits = 1 + parameter; totalBits = interestingBits + msbs; pattern = 1 << parameter; /* the unary end bit */ pattern |= (uval & ((1 << parameter) - 1)); /* the binary LSBs */ if (totalBits <= 32) { writeRawUInt(pattern, totalBits); } else { /* write the unary MSBs */ writeZeroes(msbs); /* write the unary end bit and binary LSBs */ writeRawUInt(pattern, interestingBits); } } public void writeUTF8UInt(int val) throws IOException { if (val < 0x80) { writeRawUInt(val, 8); } else if (val < 0x800) { writeRawUInt(0xC0 | (val >> 6), 8); writeRawUInt(0x80 | (val & 0x3F), 8); } else if (val < 0x10000) { writeRawUInt(0xE0 | (val >> 12), 8); writeRawUInt(0x80 | ((val >> 6) & 0x3F), 8); writeRawUInt(0x80 | (val & 0x3F), 8); } else if (val < 0x200000) { writeRawUInt(0xF0 | (val >> 18), 8); writeRawUInt(0x80 | ((val >> 12) & 0x3F), 8); writeRawUInt(0x80 | ((val >> 6) & 0x3F), 8); writeRawUInt(0x80 | (val & 0x3F), 8); } else if (val < 0x4000000) { writeRawUInt(0xF8 | (val >> 24), 8); writeRawUInt(0x80 | ((val >> 18) & 0x3F), 8); writeRawUInt(0x80 | ((val >> 12) & 0x3F), 8); writeRawUInt(0x80 | ((val >> 6) & 0x3F), 8); writeRawUInt(0x80 | (val & 0x3F), 8); } else { writeRawUInt(0xFC | (val >> 30), 8); writeRawUInt(0x80 | ((val >> 24) & 0x3F), 8); writeRawUInt(0x80 | ((val >> 18) & 0x3F), 8); writeRawUInt(0x80 | ((val >> 12) & 0x3F), 8); writeRawUInt(0x80 | ((val >> 6) & 0x3F), 8); writeRawUInt(0x80 | (val & 0x3F), 8); } } public void writeUTF8ULong(long val) throws IOException { if (val < 0x80) { writeRawUInt((int) val, 8); } else if (val < 0x800) { writeRawUInt(0xC0 | (int) (val >> 6), 8); writeRawUInt(0x80 | (int) (val & 0x3F), 8); } else if (val < 0x10000) { writeRawUInt(0xE0 | (int) (val >> 12), 8); writeRawUInt(0x80 | (int) ((val >> 6) & 0x3F), 8); writeRawUInt(0x80 | (int) (val & 0x3F), 8); } else if (val < 0x200000) { writeRawUInt(0xF0 | (int) (val >> 18), 8); writeRawUInt(0x80 | (int) ((val >> 12) & 0x3F), 8); writeRawUInt(0x80 | (int) ((val >> 6) & 0x3F), 8); writeRawUInt(0x80 | (int) (val & 0x3F), 8); } else if (val < 0x4000000) { writeRawUInt(0xF8 | (int) (val >> 24), 8); writeRawUInt(0x80 | (int) ((val >> 18) & 0x3F), 8); writeRawUInt(0x80 | (int) ((val >> 12) & 0x3F), 8); writeRawUInt(0x80 | (int) ((val >> 6) & 0x3F), 8); writeRawUInt(0x80 | (int) (val & 0x3F), 8); } else if (val < 0x80000000) { writeRawUInt(0xFC | (int) (val >> 30), 8); writeRawUInt(0x80 | (int) ((val >> 24) & 0x3F), 8); writeRawUInt(0x80 | (int) ((val >> 18) & 0x3F), 8); writeRawUInt(0x80 | (int) ((val >> 12) & 0x3F), 8); writeRawUInt(0x80 | (int) ((val >> 6) & 0x3F), 8); writeRawUInt(0x80 | (int) (val & 0x3F), 8); } else { writeRawUInt(0xFE, 8); writeRawUInt(0x80 | (int) ((val >> 30) & 0x3F), 8); writeRawUInt(0x80 | (int) ((val >> 24) & 0x3F), 8); writeRawUInt(0x80 | (int) ((val >> 18) & 0x3F), 8); writeRawUInt(0x80 | (int) ((val >> 12) & 0x3F), 8); writeRawUInt(0x80 | (int) ((val >> 6) & 0x3F), 8); writeRawUInt(0x80 | (int) (val & 0x3F), 8); } } /** * Write zero bits to byte boundry. * * @throws IOException On error writing to bit stream */ public void zeroPadToByteBoundary() throws IOException { // 0-pad to byte boundary if ((outBits & 7) != 0) writeZeroes(8 - (outBits & 7)); } /** * Flush bit stream after aligning byte boundry. * * @throws IOException On error writing. */ public void flushByteAligned() throws IOException { zeroPadToByteBoundary(); if (outBlurbs == 0) return; os.write(buffer, 0, outBlurbs); outBlurbs = 0; } /* * DRR FIX boolean peek_bit(unsigned * val, boolean(* read_callback) (byte * buffer[], unsigned * bytes, void * client_data), void * client_data) { * * while (1) { if (total_consumed_bits < total_bits) { val = * (buffer[consumed_blurbs] & BLURB_BIT_TO_MASK(consumed_bits)) ? 1 : 0; * return true; } else { if (!read_from_client_(bb, read_callback, * client_data)) return false; } } } */ /* * # ifdef SYMMETRIC_RICE boolean read_symmetric_rice_signed( BitBuffer8 * * bb, int * val, unsigned parameter, boolean(* read_callback) (byte * buffer[], unsigned * bytes, void * client_data), void * client_data) { * uint32 sign = 0, lsbs = 0, msbs = 0; * * ASSERT(0 != bb); ASSERT(0 != buffer); ASSERT(parameter <= 31); // read * the unary MSBs and end bit if (!read_unary_unsigned(bb, & msbs, * read_callback, client_data)) return false; // read the sign bit if * (!read_bit_to_uint32(bb, & sign, read_callback, client_data)) return * false; // read the binary LSBs if (!read_raw_uint32(bb, & lsbs, * parameter, read_callback, client_data)) return false; // compose the * value val = (msbs < < parameter) | lsbs; if (sign) val = - (* val); * * return true; } # endif // ifdef SYMMETRIC_RICE * * boolean read_rice_signed( BitBuffer8 * bb, int * val, unsigned parameter, * boolean(* read_callback) (byte buffer[], unsigned * bytes, void * * client_data), void * client_data) { uint32 lsbs = 0, msbs = 0; unsigned * uval; * * ASSERT(0 != bb); ASSERT(0 != buffer); ASSERT(parameter <= 31); // read * the unary MSBs and end bit if (!read_unary_unsigned(bb, & msbs, * read_callback, client_data)) return false; // read the binary LSBs if * (!read_raw_uint32(bb, & lsbs, parameter, read_callback, client_data)) * return false; // compose the value uval = (msbs < < parameter) | lsbs; if * (uval & 1) val = - ((int) (uval >> 1)) - 1; else val = (int) (uval >> 1); * * return true; } */ /** * Returns the totalBits. * * @return Returns the totalBits. */ public int getTotalBits() { return totalBits; } /** * Returns the totalBlurbs. * * @return Returns the totalBlurbs. */ public int getTotalBlurbs() { return (totalBits + 7) / 8; } }