package org.jaudiotagger.tag.id3; import java.nio.ByteBuffer; /** * Peforms encoding/decoding of an syncsafe integer * <p/> * <p>Syncsafe integers are used for the size in the tag header of v23 and v24 tags, and in the frame size in * the frame header of v24 frames. * <p/> * <p>In some parts of the tag it is inconvenient to use the * unsychronisation scheme because the size of unsynchronised data is * not known in advance, which is particularly problematic with size * descriptors. The solution in ID3v2 is to use synchsafe integers, in * which there can never be any false synchs. Synchsafe integers are * integers that keep its highest bit (bit 7) zeroed, making seven bits * out of eight available. Thus a 32 bit synchsafe integer can store 28 * bits of information. * <p/> * Example: * <p/> * 255 (%11111111) encoded as a 16 bit synchsafe integer is 383 * (%00000001 01111111). */ public class ID3SyncSafeInteger { public static final int INTEGRAL_SIZE = 4; /** * Sizes equal or smaller than this are the same whether held as sync safe integer or normal integer so * it doesnt matter. */ public static final int MAX_SAFE_SIZE = 127; /** * Read syncsafe value from byteArray in format specified in spec and convert to int. * * @param buffer syncsafe integer * @return decoded int */ private static int bufferToValue(byte[] buffer) { //Note Need to && with 0xff otherwise if value is greater than 128 we get a negative number //when cast byte to int return ((buffer[0] & 0xff) << 21) + ((buffer[1] & 0xff) << 14) + ((buffer[2] & 0xff) << 7) + ((buffer[3]) & 0xff); } /** * Read syncsafe value from buffer in format specified in spec and convert to int. * <p/> * The buffers position is moved to just after the location of the syncsafe integer * * @param buffer syncsafe integer * @return decoded int */ protected static int bufferToValue(ByteBuffer buffer) { byte byteBuffer[] = new byte[INTEGRAL_SIZE]; buffer.get(byteBuffer, 0, INTEGRAL_SIZE); return bufferToValue(byteBuffer); } /** * Is buffer holding a value that is definently not syncsafe * <p/> * We cannot guarantee a buffer is holding a syncsafe integer but there are some checks * we can do to show that it definently is not. * <p/> * The buffer is NOT moved after reading. * <p/> * This function is useful for reading ID3v24 frames created in iTunes because iTunes does not use syncsafe * integers in its frames. * * @param buffer * @return true if this buffer is definently not holding a syncsafe integer */ protected static boolean isBufferNotSyncSafe(ByteBuffer buffer) { int position = buffer.position(); //Check Bit7 not set for (int i = 0; i < INTEGRAL_SIZE; i++) { byte nextByte = buffer.get(position + i); if ((nextByte & 0x80) > 0) { return true; } } return false; } /** * Checks if the buffer just contains zeros * <p/> * This can be used to identify when accessing padding of a tag * * @param buffer * @return true if buffer only contains zeros */ protected static boolean isBufferEmpty(byte[] buffer) { for (byte aBuffer : buffer) { if (aBuffer != 0) { return false; } } return true; } /** * Convert int value to syncsafe value held in bytearray * * @param size * @return buffer syncsafe integer */ protected static byte[] valueToBuffer(int size) { byte[] buffer = new byte[4]; buffer[0] = (byte) ((size & 0x0FE00000) >> 21); buffer[1] = (byte) ((size & 0x001FC000) >> 14); buffer[2] = (byte) ((size & 0x00003F80) >> 7); buffer[3] = (byte) (size & 0x0000007F); return buffer; } }