FileDocCategorySizeDatePackage
MPEGFrameHeader.javaAPI DocJaudiotagger 2.0.427812Fri May 06 14:34:48 BST 2011org.jaudiotagger.audio.mp3

MPEGFrameHeader.java

/**
 * @author : Paul Taylor
 * <p/>
 * Version @version:$Id: MPEGFrameHeader.java 970 2011-05-06 12:34:36Z paultaylor $
 * Date :${DATE}
 * <p/>
 * Jaikoz Copyright Copyright (C) 2003 -2005 JThink Ltd
 */
package org.jaudiotagger.audio.mp3;

import org.jaudiotagger.FileConstants;
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
import org.jaudiotagger.logging.AbstractTagDisplayFormatter;

import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;


/**
 * Represents a MPEGFrameHeader, an MP3 is made up of a number of frames each frame starts with a four
 * byte frame header.
 */
@SuppressWarnings({"PointlessArithmeticExpression"})
public class MPEGFrameHeader
{
    /**
     * Constants for MP3 Frame header, each frame has a basic header of
     * 4 bytes
     */
    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;
    public static final int HEADER_SIZE = 4;

    /**
     * Sync Value to identify the start of an MPEGFrame
     */
    public static final int SYNC_SIZE = 2;

    public static final int SYNC_BYTE1 = 0xFF;
    public static final int SYNC_BYTE2 = 0xE0;
    public static final int SYNC_BIT_ANDSAMPING_BYTE3 = 0xFC;

    private static final byte[] header = new byte[HEADER_SIZE];


    /**
     * Constants for MPEG Version
     */
    public static final Map<Integer, String> mpegVersionMap = new HashMap<Integer, String>();
    public final static int VERSION_2_5 = 0;
    public final static int VERSION_2 = 2;
    public final static int VERSION_1 = 3;

    static
    {
        mpegVersionMap.put(VERSION_2_5, "MPEG-2.5");
        mpegVersionMap.put(VERSION_2, "MPEG-2");
        mpegVersionMap.put(VERSION_1, "MPEG-1");
    }

    /**
     * Constants for MPEG Layer
     */
    public static final Map<Integer, String> mpegLayerMap = new HashMap<Integer, String>();
    public final static int LAYER_I = 3;
    public final static int LAYER_II = 2;
    public final static int LAYER_III = 1;

    static
    {
        mpegLayerMap.put(LAYER_I, "Layer 1");
        mpegLayerMap.put(LAYER_II, "Layer 2");
        mpegLayerMap.put(LAYER_III, "Layer 3");
    }

    /**
     * Slot Size is dependent on Layer
     */
    public final static int LAYER_I_SLOT_SIZE = 4;
    public final static int LAYER_II_SLOT_SIZE = 1;
    public final static int LAYER_III_SLOT_SIZE = 1;

    /**
     * Bit Rates, the setBitrate varies for different Version and Layer
     */
    private static final Map<Integer, Integer> bitrateMap = new HashMap<Integer, Integer>();

    static
    {
        // MPEG-1, Layer I (E)
        bitrateMap.put(0x1E, 32);
        bitrateMap.put(0x2E, 64);
        bitrateMap.put(0x3E, 96);
        bitrateMap.put(0x4E, 128);
        bitrateMap.put(0x5E, 160);
        bitrateMap.put(0x6E, 192);
        bitrateMap.put(0x7E, 224);
        bitrateMap.put(0x8E, 256);
        bitrateMap.put(0x9E, 288);
        bitrateMap.put(0xAE, 320);
        bitrateMap.put(0xBE, 352);
        bitrateMap.put(0xCE, 384);
        bitrateMap.put(0xDE, 416);
        bitrateMap.put(0xEE, 448);
        // MPEG-1, Layer II (C)
        bitrateMap.put(0x1C, 32);
        bitrateMap.put(0x2C, 48);
        bitrateMap.put(0x3C, 56);
        bitrateMap.put(0x4C, 64);
        bitrateMap.put(0x5C, 80);
        bitrateMap.put(0x6C, 96);
        bitrateMap.put(0x7C, 112);
        bitrateMap.put(0x8C, 128);
        bitrateMap.put(0x9C, 160);
        bitrateMap.put(0xAC, 192);
        bitrateMap.put(0xBC, 224);
        bitrateMap.put(0xCC, 256);
        bitrateMap.put(0xDC, 320);
        bitrateMap.put(0xEC, 384);
        // MPEG-1, Layer III (A)
        bitrateMap.put(0x1A, 32);
        bitrateMap.put(0x2A, 40);
        bitrateMap.put(0x3A, 48);
        bitrateMap.put(0x4A, 56);
        bitrateMap.put(0x5A, 64);
        bitrateMap.put(0x6A, 80);
        bitrateMap.put(0x7A, 96);
        bitrateMap.put(0x8A, 112);
        bitrateMap.put(0x9A, 128);
        bitrateMap.put(0xAA, 160);
        bitrateMap.put(0xBA, 192);
        bitrateMap.put(0xCA, 224);
        bitrateMap.put(0xDA, 256);
        bitrateMap.put(0xEA, 320);
        // MPEG-2, Layer I (6)
        bitrateMap.put(0x16, 32);
        bitrateMap.put(0x26, 48);
        bitrateMap.put(0x36, 56);
        bitrateMap.put(0x46, 64);
        bitrateMap.put(0x56, 80);
        bitrateMap.put(0x66, 96);
        bitrateMap.put(0x76, 112);
        bitrateMap.put(0x86, 128);
        bitrateMap.put(0x96, 144);
        bitrateMap.put(0xA6, 160);
        bitrateMap.put(0xB6, 176);
        bitrateMap.put(0xC6, 192);
        bitrateMap.put(0xD6, 224);
        bitrateMap.put(0xE6, 256);
        // MPEG-2, Layer II (4)
        bitrateMap.put(0x14, 8);
        bitrateMap.put(0x24, 16);
        bitrateMap.put(0x34, 24);
        bitrateMap.put(0x44, 32);
        bitrateMap.put(0x54, 40);
        bitrateMap.put(0x64, 48);
        bitrateMap.put(0x74, 56);
        bitrateMap.put(0x84, 64);
        bitrateMap.put(0x94, 80);
        bitrateMap.put(0xA4, 96);
        bitrateMap.put(0xB4, 112);
        bitrateMap.put(0xC4, 128);
        bitrateMap.put(0xD4, 144);
        bitrateMap.put(0xE4, 160);
        // MPEG-2, Layer III (2)
        bitrateMap.put(0x12, 8);
        bitrateMap.put(0x22, 16);
        bitrateMap.put(0x32, 24);
        bitrateMap.put(0x42, 32);
        bitrateMap.put(0x52, 40);
        bitrateMap.put(0x62, 48);
        bitrateMap.put(0x72, 56);
        bitrateMap.put(0x82, 64);
        bitrateMap.put(0x92, 80);
        bitrateMap.put(0xA2, 96);
        bitrateMap.put(0xB2, 112);
        bitrateMap.put(0xC2, 128);
        bitrateMap.put(0xD2, 144);
        bitrateMap.put(0xE2, 160);
    }

    /**
     * Constants for Channel mode
     */
    public static final Map<Integer, String> modeMap = new HashMap<Integer, String>();
    public final static int MODE_STEREO = 0;
    public final static int MODE_JOINT_STEREO = 1;
    public final static int MODE_DUAL_CHANNEL = 2;
    public final static int MODE_MONO = 3;

    static
    {
        modeMap.put(MODE_STEREO, "Stereo");
        modeMap.put(MODE_JOINT_STEREO, "Joint Stereo");
        modeMap.put(MODE_DUAL_CHANNEL, "Dual");
        modeMap.put(MODE_MONO, "Mono");
    }

    /**
     * Constants for Emphasis
     */
    private static final Map<Integer, String> emphasisMap = new HashMap<Integer, String>();
    public final static int EMPHASIS_NONE = 0;
    public final static int EMPHASIS_5015MS = 1;
    public final static int EMPHASIS_RESERVED = 2;
    public final static int EMPHASIS_CCITT = 3;

    static
    {
        emphasisMap.put(EMPHASIS_NONE, "None");
        emphasisMap.put(EMPHASIS_5015MS, "5015MS");
        emphasisMap.put(EMPHASIS_RESERVED, "Reserved");
        emphasisMap.put(EMPHASIS_CCITT, "CCITT");
    }


    private static final Map<Integer, String> modeExtensionMap = new HashMap<Integer, String>();
    private final static int MODE_EXTENSION_NONE = 0;
    private final static int MODE_EXTENSION_ONE = 1;
    private final static int MODE_EXTENSION_TWO = 2;
    private final static int MODE_EXTENSION_THREE = 3;

    private static final Map<Integer, String> modeExtensionLayerIIIMap = new HashMap<Integer, String>();
    private final static int MODE_EXTENSION_OFF_OFF = 0;
    private final static int MODE_EXTENSION_ON_OFF = 1;
    private final static int MODE_EXTENSION_OFF_ON = 2;
    private final static int MODE_EXTENSION_ON_ON = 3;

    static
    {
        modeExtensionMap.put(MODE_EXTENSION_NONE, "4-31");
        modeExtensionMap.put(MODE_EXTENSION_ONE, "8-31");
        modeExtensionMap.put(MODE_EXTENSION_TWO, "12-31");
        modeExtensionMap.put(MODE_EXTENSION_THREE, "16-31");

        modeExtensionLayerIIIMap.put(MODE_EXTENSION_OFF_OFF, "off-off");
        modeExtensionLayerIIIMap.put(MODE_EXTENSION_ON_OFF, "on-off");
        modeExtensionLayerIIIMap.put(MODE_EXTENSION_OFF_ON, "off-on");
        modeExtensionLayerIIIMap.put(MODE_EXTENSION_ON_ON, "on-on");
    }

    /**
     * Sampling Rate in Hz
     */
    private static final Map<Integer, Map<Integer, Integer>> samplingRateMap = new HashMap<Integer, Map<Integer, Integer>>();
    private static final Map<Integer, Integer> samplingV1Map = new HashMap<Integer, Integer>();
    private static final Map<Integer, Integer> samplingV2Map = new HashMap<Integer, Integer>();
    private static final Map<Integer, Integer> samplingV25Map = new HashMap<Integer, Integer>();

    static
    {
        samplingV1Map.put(0, 44100);
        samplingV1Map.put(1, 48000);
        samplingV1Map.put(2, 32000);

        samplingV2Map.put(0, 22050);
        samplingV2Map.put(1, 24000);
        samplingV2Map.put(2, 16000);

        samplingV25Map.put(0, 11025);
        samplingV25Map.put(1, 12000);
        samplingV25Map.put(2, 8000);

        samplingRateMap.put(VERSION_1, samplingV1Map);
        samplingRateMap.put(VERSION_2, samplingV2Map);
        samplingRateMap.put(VERSION_2_5, samplingV25Map);
    }

    /* Samples Per Frame */
    private static final Map<Integer, Map<Integer, Integer>> samplesPerFrameMap = new HashMap<Integer, Map<Integer, Integer>>();
    private static final Map<Integer, Integer> samplesPerFrameV1Map = new HashMap<Integer, Integer>();
    private static final Map<Integer, Integer> samplesPerFrameV2Map = new HashMap<Integer, Integer>();
    private static final Map<Integer, Integer> samplesPerFrameV25Map = new HashMap<Integer, Integer>();

    static
    {
        samplesPerFrameV1Map.put(LAYER_I, 384);
        samplesPerFrameV1Map.put(LAYER_II, 1152);
        samplesPerFrameV1Map.put(LAYER_III, 1152);

        samplesPerFrameV2Map.put(LAYER_I, 384);
        samplesPerFrameV2Map.put(LAYER_II, 1152);
        samplesPerFrameV2Map.put(LAYER_III, 576);

        samplesPerFrameV25Map.put(LAYER_I, 384);
        samplesPerFrameV25Map.put(LAYER_II, 1152);
        samplesPerFrameV25Map.put(LAYER_III, 576);

        samplesPerFrameMap.put(VERSION_1, samplesPerFrameV1Map);
        samplesPerFrameMap.put(VERSION_2, samplesPerFrameV2Map);
        samplesPerFrameMap.put(VERSION_2_5, samplesPerFrameV25Map);

    }


    private static final int SCALE_BY_THOUSAND = 1000;
    private static final int LAYER_I_FRAME_SIZE_COEFFICIENT = 12;
    private static final int LAYER_II_FRAME_SIZE_COEFFICIENT = 144;
    private static final int LAYER_III_FRAME_SIZE_COEFFICIENT = 144;

    /**
     * MP3 Frame Header bit mask
     */
    private static final int MASK_MP3_ID = FileConstants.BIT3;

    /**
     * MP3 version, confusingly for MP3s the version is 1.
     */
    private static final int MASK_MP3_VERSION = FileConstants.BIT4 | FileConstants.BIT3;

    /**
     * MP3 Layer, for MP3s the Layer is 3
     */
    private static final int MASK_MP3_LAYER = FileConstants.BIT2 | FileConstants.BIT1;

    /**
     * Does it include a CRC Checksum at end of header, this can be used to check the header.
     */
    private static final int MASK_MP3_PROTECTION = FileConstants.BIT0;

    /**
     * The setBitrate of this MP3
     */
    private static final int MASK_MP3_BITRATE = FileConstants.BIT7 | FileConstants.BIT6 | FileConstants.BIT5 | FileConstants.BIT4;

    /**
     * The sampling/frequency rate
     */
    private static final int MASK_MP3_FREQUENCY = FileConstants.BIT3 + FileConstants.BIT2;

    /**
     * An extra padding bit is sometimes used to make sure frames are exactly the right length
     */
    private static final int MASK_MP3_PADDING = FileConstants.BIT1;

    /**
     * Private bit set, for application specific
     */
    private static final int MASK_MP3_PRIVACY = FileConstants.BIT0;

    /**
     * Channel Mode, Stero/Mono/Dual Channel
     */
    private static final int MASK_MP3_MODE = FileConstants.BIT7 | FileConstants.BIT6;

    /**
     * MP3 Frame Header bit mask
     */
    private static final int MASK_MP3_MODE_EXTENSION = FileConstants.BIT5 | FileConstants.BIT4;

    /**
     * MP3 Frame Header bit mask
     */
    private static final int MASK_MP3_COPY = FileConstants.BIT3;

    /**
     * MP3 Frame Header bit mask
     */
    private static final int MASK_MP3_HOME = FileConstants.BIT2;

    /**
     * MP3 Frame Header bit mask
     */
    private static final int MASK_MP3_EMPHASIS = FileConstants.BIT1 | FileConstants.BIT0;


    private byte[] mpegBytes;

    /**
     * The version of this MPEG frame (see the constants)
     */
    private int version;

    private String versionAsString;

    /**
     * Contains the mpeg layer of this frame (see constants)
     */
    private int layer;

    private String layerAsString;
    /**
     * Bitrate of this frame
     */
    private Integer bitRate;

    /**
     * Channel Mode of this Frame (see constants)
     */
    private int channelMode;

    /**
     * Channel Mode of this Frame As English String
     */
    private String channelModeAsString;

    /**
     * Emphasis of this frame
     */
    private int emphasis;

    /**
     * Emphasis mode string
     */
    private String emphasisAsString;

    /**
     * Mode Extension
     */
    private String modeExtension;

    /**
     * Flag indicating if this frame has padding byte
     */
    private boolean isPadding;

    /**
     * Flag indicating if this frame contains copyrighted material
     */
    private boolean isCopyrighted;

    /**
     * Flag indicating if this frame contains original material
     */
    private boolean isOriginal;

    /**
     * Flag indicating if this frame is protected
     */
    private boolean isProtected;


    /**
     * Flag indicating if this frame is private
     */
    private boolean isPrivate;

    private Integer samplingRate;


    /**
     * Gets the layerVersion attribute of the MPEGFrame object
     *
     * @return The layerVersion value
     */
    public int getLayer()
    {
        return layer;
    }

    public String getLayerAsString()
    {
        return layerAsString;
    }

    /**
     * Gets the copyrighted attribute of the MPEGFrame object
     */
    private void setCopyrighted()
    {
        isCopyrighted = (mpegBytes[BYTE_4] & MASK_MP3_COPY) != 0;
    }


    /**
     * Set the version of this frame as an int value (see constants)
     * @throws org.jaudiotagger.audio.exceptions.InvalidAudioFrameException
     */
    private void setVersion() throws InvalidAudioFrameException
    {
        //MPEG Version
        version = (byte) ((mpegBytes[BYTE_2] & MASK_MP3_VERSION) >> 3);
        versionAsString = mpegVersionMap.get(version);
        if (versionAsString == null)
        {
            throw new InvalidAudioFrameException("Invalid mpeg version");
        }
    }

    /**
     * Sets the original attribute of the MPEGFrame object
     */
    private void setOriginal()
    {
        isOriginal = (mpegBytes[BYTE_4] & MASK_MP3_HOME) != 0;
    }

    /**
     * Sets the protected attribute of the MPEGFrame object
     */
    private void setProtected()
    {
        isProtected = (mpegBytes[BYTE_2] & MASK_MP3_PROTECTION) == 0x00;
    }

    /**
     * Sets the private attribute of the MPEGFrame object
     */
    private void setPrivate()
    {
        isPrivate = (mpegBytes[BYTE_3] & MASK_MP3_PRIVACY) != 0;
    }

    /**
     * Get the setBitrate of this frame
     * @throws org.jaudiotagger.audio.exceptions.InvalidAudioFrameException
     */
    private void setBitrate() throws InvalidAudioFrameException
    {
        /* BitRate, get by checking header setBitrate bits and MPEG Version and Layer */
        int bitRateIndex = mpegBytes[BYTE_3] & MASK_MP3_BITRATE | mpegBytes[BYTE_2] & MASK_MP3_ID | mpegBytes[BYTE_2] & MASK_MP3_LAYER;

        bitRate = bitrateMap.get(bitRateIndex);
        if (bitRate == null)
        {
            throw new InvalidAudioFrameException("Invalid bitrate");
        }
    }


    /**
     * Set the Mpeg channel mode of this frame as a constant (see constants)
     * @throws org.jaudiotagger.audio.exceptions.InvalidAudioFrameException
     */
    private void setChannelMode() throws InvalidAudioFrameException
    {
        channelMode = (mpegBytes[BYTE_4] & MASK_MP3_MODE) >>> 6;
        channelModeAsString = modeMap.get(channelMode);
        if (channelModeAsString == null)
        {
            throw new InvalidAudioFrameException("Invalid channel mode");
        }
    }

    /**
     * Get the setEmphasis mode of this frame in a string representation
     * @throws org.jaudiotagger.audio.exceptions.InvalidAudioFrameException
     */
    private void setEmphasis() throws InvalidAudioFrameException
    {
        emphasis = mpegBytes[BYTE_4] & MASK_MP3_EMPHASIS;
        emphasisAsString = emphasisMap.get(emphasis);
        if (getEmphasisAsString() == null)
        {
            throw new InvalidAudioFrameException("Invalid emphasis");
        }
    }


    /**
     * Set whether this frame uses padding bytes
     */
    private void setPadding()
    {
        isPadding = (mpegBytes[BYTE_3] & MASK_MP3_PADDING) != 0;
    }


    /**
     * Get the layer version of this frame as a constant int value (see constants)
     * @throws org.jaudiotagger.audio.exceptions.InvalidAudioFrameException
     */
    private void setLayer() throws InvalidAudioFrameException
    {
        layer = (mpegBytes[BYTE_2] & MASK_MP3_LAYER) >>> 1;
        layerAsString = mpegLayerMap.get(layer);
        if (layerAsString == null)
        {
            throw new InvalidAudioFrameException("Invalid Layer");
        }
    }


    /**
     * Sets the string representation of the mode extension of this frame
     * @throws org.jaudiotagger.audio.exceptions.InvalidAudioFrameException
     */
    private void setModeExtension() throws InvalidAudioFrameException
    {
        int index = (mpegBytes[BYTE_4] & MASK_MP3_MODE_EXTENSION) >> 4;
        if (layer == LAYER_III)
        {
            modeExtension = modeExtensionLayerIIIMap.get(index);
            if (getModeExtension() == null)
            {
                throw new InvalidAudioFrameException("Invalid Mode Extension");
            }
        }
        else
        {
            modeExtension = modeExtensionMap.get(index);
            if (getModeExtension() == null)
            {
                throw new InvalidAudioFrameException("Invalid Mode Extension");
            }
        }
    }

    /**
     * set the sampling rate in Hz of this frame
     * @throws org.jaudiotagger.audio.exceptions.InvalidAudioFrameException
     */
    private void setSamplingRate() throws InvalidAudioFrameException
    {
        //Frequency
        int index = (mpegBytes[BYTE_3] & MASK_MP3_FREQUENCY) >>> 2;
        Map<Integer, Integer> samplingRateMapForVersion = samplingRateMap.get(version);
        if (samplingRateMapForVersion == null)
        {
            throw new InvalidAudioFrameException("Invalid version");
        }
        samplingRate = samplingRateMapForVersion.get(index);
        if (samplingRate == null)
        {
            throw new InvalidAudioFrameException("Invalid sampling rate");
        }
    }

    /**
     * Gets the number of channels
     *
     * @return The setChannelMode value
     */
    public int getNumberOfChannels()
    {
        switch (channelMode)
        {
            case MODE_DUAL_CHANNEL:
                return 2;
            case MODE_JOINT_STEREO:
                return 2;
            case MODE_MONO:
                return 1;
            case MODE_STEREO:
                return 2;
            default:
                return 0;
        }
    }

    public int getChannelMode()
    {
        return channelMode;
    }

    public String getChannelModeAsString()
    {
        return channelModeAsString;
    }

    /**
     * Gets the mPEGVersion attribute of the MPEGFrame object
     *
     * @return The mPEGVersion value
     */
    public int getVersion()
    {
        return version;
    }

    public String getVersionAsString()
    {
        return versionAsString;
    }

    /**
     * Gets the paddingLength attribute of the MPEGFrame object
     *
     * @return The paddingLength value
     */
    public int getPaddingLength()
    {
        if (isPadding())
        {
            return 1;
        }
        else
        {
            return 0;
        }
    }

    public Integer getBitRate()
    {
        return bitRate;
    }

    public Integer getSamplingRate()
    {
        return samplingRate;
    }

    /*
     * Gets this frame length in bytes, value should always be rounded down to the nearest byte (not rounded up)
     *
     * Calculation is Bitrate (scaled to bps) divided by sampling frequency (in Hz), The larger the bitrate the larger
     * the frame but the more samples per second the smaller the value, also have to take into account frame padding
     * Have to multiple by a coefficient constant depending upon the layer it is encoded in,

     */
    public int getFrameLength()
    {
        switch (version)
        {
            case VERSION_2:
            case VERSION_2_5:
                switch (layer)
                {
                    case LAYER_I:
                        return (LAYER_I_FRAME_SIZE_COEFFICIENT * (getBitRate() * SCALE_BY_THOUSAND) / getSamplingRate() + getPaddingLength()) * LAYER_I_SLOT_SIZE;

                    case LAYER_II:
                        return (LAYER_II_FRAME_SIZE_COEFFICIENT ) * (getBitRate() * SCALE_BY_THOUSAND) / getSamplingRate() + getPaddingLength() * LAYER_II_SLOT_SIZE;

                    case LAYER_III:
                        if (this.getChannelMode() == MODE_MONO)
                        {
                            return (LAYER_III_FRAME_SIZE_COEFFICIENT / 2 ) * (getBitRate() * SCALE_BY_THOUSAND) / getSamplingRate() + getPaddingLength() * LAYER_III_SLOT_SIZE;
                        }
                        else
                        {
                            return (LAYER_III_FRAME_SIZE_COEFFICIENT) * (getBitRate() * SCALE_BY_THOUSAND) / getSamplingRate() + getPaddingLength() * LAYER_III_SLOT_SIZE;
                        }


                    default:
                        throw new RuntimeException("Mp3 Unknown Layer:" + layer);

                }


            case VERSION_1:
                switch (layer)
                {
                    case LAYER_I:
                        return (LAYER_I_FRAME_SIZE_COEFFICIENT * (getBitRate() * SCALE_BY_THOUSAND) / getSamplingRate() + getPaddingLength()) * LAYER_I_SLOT_SIZE;

                    case LAYER_II:
                        return LAYER_II_FRAME_SIZE_COEFFICIENT * (getBitRate() * SCALE_BY_THOUSAND) / getSamplingRate() + getPaddingLength() * LAYER_II_SLOT_SIZE;

                    case LAYER_III:
                        return LAYER_III_FRAME_SIZE_COEFFICIENT * (getBitRate() * SCALE_BY_THOUSAND) / getSamplingRate() + getPaddingLength() * LAYER_III_SLOT_SIZE;

                    default:
                        throw new RuntimeException("Mp3 Unknown Layer:" + layer);

                }

            default:
                throw new RuntimeException("Mp3 Unknown Version:" + version);

        }
    }

    /**
     * Get the number of samples in a frame, all frames in a file have a set number of samples as defined by their MPEG Versiona
     * and Layer
     * @return
     */
    public int getNoOfSamples()
    {
        Integer noOfSamples = samplesPerFrameMap.get(version).get(layer);
        return noOfSamples;
    }


    public boolean isPadding()
    {
        return isPadding;
    }

    public boolean isCopyrighted()
    {
        return isCopyrighted;
    }

    public boolean isOriginal()
    {
        return isOriginal;
    }

    public boolean isProtected()
    {
        return isProtected;
    }

    public boolean isPrivate()
    {
        return isPrivate;
    }

    public boolean isVariableBitRate()
    {
        return false;
    }

    public int getEmphasis()
    {
        return emphasis;
    }

    public String getEmphasisAsString()
    {
        return emphasisAsString;
    }

    public String getModeExtension()
    {
        return modeExtension;
    }


    /**
     * Hide Constructor
     * @throws org.jaudiotagger.audio.exceptions.InvalidAudioFrameException
     */
    private MPEGFrameHeader() throws InvalidAudioFrameException
    {

    }

    /**
     * Try and create a new MPEG frame with the given byte array and decodes its contents
     * If decoding header causes a problem it is not a valid header
     *
     * @param b the array of bytes representing this mpeg frame
     * @throws InvalidAudioFrameException if does not match expected format
     */
    private MPEGFrameHeader(byte[] b) throws InvalidAudioFrameException
    {
        mpegBytes = b;
        setBitrate();
        setVersion();
        setLayer();
        setProtected();
        setSamplingRate();
        setPadding();
        setPrivate();
        setChannelMode();
        setModeExtension();
        setCopyrighted();
        setOriginal();
        setEmphasis();
    }

    /**
     * Parse the MPEGFrameHeader of an MP3File, file pointer returns at end of the frame header
     *
     * @param bb the byte buffer containing the header
     * @return
     * @throws InvalidAudioFrameException if there is no header at this point
     */
    public static MPEGFrameHeader parseMPEGHeader(ByteBuffer bb) throws InvalidAudioFrameException
    {
        int position = bb.position();
        bb.get(header, 0, HEADER_SIZE);
        bb.position(position);
        MPEGFrameHeader frameHeader = new MPEGFrameHeader(header);

        return frameHeader;
    }

    /**
     * Gets the MPEGFrame attribute of the MPEGFrame object
     *
     * @param bb
     * @return The mPEGFrame value
     */
    public static boolean isMPEGFrame(ByteBuffer bb)
    {
        int position = bb.position();
        return (((bb.get(position) & SYNC_BYTE1) == SYNC_BYTE1)
                && ((bb.get(position + 1) & SYNC_BYTE2) == SYNC_BYTE2)
                && ((bb.get(position + 2) & SYNC_BIT_ANDSAMPING_BYTE3) != SYNC_BIT_ANDSAMPING_BYTE3));
    }

    /**
     * @return a string represntation
     */
    public String toString()
    {
        return " mpeg frameheader:" + " frame length:" + getFrameLength() + " version:" + versionAsString + " layer:" + layerAsString + " channelMode:" + channelModeAsString + " noOfSamples:" + getNoOfSamples() + " samplingRate:" + samplingRate + " isPadding:" + isPadding + " isProtected:" + isProtected + " isPrivate:" + isPrivate + " isCopyrighted:" + isCopyrighted + " isOriginal:" + isCopyrighted + " isVariableBitRate" + this.isVariableBitRate() + " header as binary:" + AbstractTagDisplayFormatter.displayAsBinary(mpegBytes[BYTE_1]) + " " + AbstractTagDisplayFormatter.displayAsBinary(mpegBytes[BYTE_2]) + " " + AbstractTagDisplayFormatter.displayAsBinary(mpegBytes[BYTE_3]) + " " + AbstractTagDisplayFormatter.displayAsBinary(mpegBytes[BYTE_4]);
    }
}