package org.jaudiotagger.audio.mp3; import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException; import java.nio.ByteBuffer; import java.util.Arrays; /** * Vrbi Frame * <p/> * <p>In MP3s encoded using the franhofer encoder which variable bit rate the first frame in the file contains a * special frame called a Vrbi Frame, instead of audio data (Other Vbr encoders use the more common Xing Frame). * This is used to store additional information about the file. The most important aspect for * this library is details allowing us to determine the bitrate of a Variable Bit Rate VBR file without having * to process the whole file. * <p/> * From http://www.codeproject.com/KB/audio-video/mpegaudioinfo.aspx#SideInfo * <p/> * This header is only used by MPEG audio files encoded with the Fraunhofer Encoder as far as I know. It is different from the XING header. You find it exactly * 32 bytes after the end of the first MPEG audio header in the file. (Note that the position is zero-based; position, length and example are each in byte-format.) * Position Length Meaning Example * 0 4 VBR header ID in 4 ASCII chars, always 'VBRI', not NULL-terminated 'VBRI' * 4 2 Version ID as Big-Endian WORD 1 * 6 2 Delay as Big-Endian float 7344 * 8 2 Quality indicator 75 * 10 4 Number of Bytes of Audio as Big-Endian DWORD 45000 * 14 4 Number of Frames as Big-Endian DWORD 7344 * 18 2 Number of entries within TOC table as Big-Endian WORD 100 * 20 2 Scale factor of TOC table entries as Big-Endian DWORD 1 * 22 2 Size per table entry in bytes (max 4) as Big-Endian WORD 2 * 24 2 Frames per table entry as Big-Endian WORD 845 * 26 TOC entries for seeking as Big-Endian integral. From size per table entry and number of entries, you can calculate the length of this field. * <p/> */ public class VbriFrame { //The offset into frame private static final int VBRI_OFFSET = MPEGFrameHeader.HEADER_SIZE + 32; private static final int VBRI_HEADER_BUFFER_SIZE = 120; //TODO this is just a guess, not right private static final int VBRI_IDENTIFIER_BUFFER_SIZE = 4; private static final int VBRI_DELAY_BUFFER_SIZE = 2; private static final int VBRI_QUALITY_BUFFER_SIZE = 2; private static final int VBRI_AUDIOSIZE_BUFFER_SIZE = 4; private static final int VBRI_FRAMECOUNT_BUFFER_SIZE = 4; private static final int VBRI_TOC_ENTRY_BUFFER_SIZE = 2; public static final int MAX_BUFFER_SIZE_NEEDED_TO_READ_VBRI = VBRI_OFFSET + VBRI_HEADER_BUFFER_SIZE; private static final int BYTE_1 = 0; private static final int BYTE_2 = 1; private static final int BYTE_3 = 2; private static final int BYTE_4 = 3; /** * Identifier */ private static final byte[] VBRI_VBR_ID = {'V', 'B', 'R', 'I'}; private static ByteBuffer header; private boolean vbr = false; private int frameCount = -1; private int audioSize = -1; private LameFrame lameFrame; /** * Read the VBRI Properties from the buffer */ private VbriFrame() { //Go to start of Buffer header.rewind(); header.position(10); setAudioSize(); setFrameCount(); } /** * Set size of AudioData */ private void setAudioSize() { byte frameSizeBuffer[] = new byte[VBRI_AUDIOSIZE_BUFFER_SIZE]; header.get(frameSizeBuffer); boolean audioSizeEnabled = true; audioSize = (frameSizeBuffer[BYTE_1] << 24) & 0xFF000000 | (frameSizeBuffer[BYTE_2] << 16) & 0x00FF0000 | (frameSizeBuffer[BYTE_3] << 8) & 0x0000FF00 | frameSizeBuffer[BYTE_4] & 0x000000FF; } /** * Set count of frames */ private void setFrameCount() { byte frameCountBuffer[] = new byte[VBRI_FRAMECOUNT_BUFFER_SIZE]; header.get(frameCountBuffer); boolean frameCountEnabled = true; frameCount = (frameCountBuffer[BYTE_1] << 24) & 0xFF000000 | (frameCountBuffer[BYTE_2] << 16) & 0x00FF0000 | (frameCountBuffer[BYTE_3] << 8) & 0x0000FF00 | frameCountBuffer[BYTE_4] & 0x000000FF; } /** * @return count of frames */ public final int getFrameCount() { return frameCount; } /** * @return size of audio data in bytes */ public final int getAudioSize() { return audioSize; } /** * Parse the VBRIFrame of an MP3File, cannot be called until we have validated that * this is a VBRIFrame * * @return * @throws org.jaudiotagger.audio.exceptions.InvalidAudioFrameException * */ public static VbriFrame parseVBRIFrame() throws InvalidAudioFrameException { VbriFrame VBRIFrame = new VbriFrame(); return VBRIFrame; } /** * IS this a VBRI frame * * @param bb * @param mpegFrameHeader * @return true if this is a VBRI frame */ public static boolean isVbriFrame(ByteBuffer bb, MPEGFrameHeader mpegFrameHeader) { //We store this so can return here after scanning through buffer int startPosition = bb.position(); // MP3File.//logger.finest("Checking VBRI Frame at" + startPosition); bb.position(startPosition + VBRI_OFFSET); //Create header from here header = bb.slice(); // Return Buffer to start Point bb.position(startPosition); //Check Identifier byte[] identifier = new byte[VBRI_IDENTIFIER_BUFFER_SIZE]; header.get(identifier); if ((!Arrays.equals(identifier, VBRI_VBR_ID))) { return false; } // MP3File.//logger.finest("Found VBRI Frame"); return true; } /** * Is this VBRIFrame detailing a varaible bit rate MPEG * * @return */ public final boolean isVbr() { return true; } public String getEncoder() { return "Fraunhofer"; } /** * @return a string represntation */ public String toString() { return "VBRIheader" + " vbr:" + vbr + " frameCount:" + frameCount + " audioFileSize:" + audioSize + " encoder:" + getEncoder(); } }