/* * 11/19/04 : 1.0 moved to LGPL. * VBRI header support added, E.B javalayer@javazoom.net * * 12/04/03 : VBR (XING) header support added, E.B javalayer@javazoom.net * * 02/13/99 : Java Conversion by JavaZOOM , E.B javalayer@javazoom.net * * Declarations for MPEG header class * A few layer III, MPEG-2 LSF, and seeking modifications made by Jeff Tsay. * Last modified : 04/19/97 * * @(#) header.h 1.7, last edit: 6/15/94 16:55:33 * @(#) Copyright (C) 1993, 1994 Tobias Bading (bading@cs.tu-berlin.de) * @(#) Berlin University of Technology *----------------------------------------------------------------------- * This program 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 program 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 program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *---------------------------------------------------------------------- */ package org.geogebra.desktop.sound.mp3transform; import java.io.IOException; import org.geogebra.common.util.Charsets; /** * Class for extracting information from a frame header. */ public final class Header { private static final int[][] FREQUENCIES = { { 22050, 24000, 16000, 1 }, { 44100, 48000, 32000, 1 }, { 11025, 12000, 8000, 1 } }; static final int VERSION_MPEG2_LSF = 0; static final int VERSION_MPEG25_LSF = 2; static final int VERSION_MPEG1 = 1; static final int MODE_JOINT_STEREO = 1; public static final int MODE_SINGLE_CHANNEL = 3; private static final int SAMPLE_FREQUENCY_FOURTYEIGHT = 1; private static final int SAMPLE_FREQUENCY_THIRTYTWO = 2; private boolean protectionBit, paddingBit; private int bitrateIndex, modeExtension; private int version; private int mode; private int sampleFrequency; private int numberOfSubbands, intensityStereoBound; private byte syncMode = Bitstream.INITIAL_SYNC; private int frameSize; private boolean vbr; private int slots; boolean readHeader(Bitstream stream) throws IOException { while (true) { int headerString = stream.syncHeader(syncMode); if (syncMode == Bitstream.INITIAL_SYNC) { version = ((headerString >>> 19) & 1); if (((headerString >>> 20) & 1) == 0) { if (version == VERSION_MPEG2_LSF) { version = VERSION_MPEG25_LSF; } else { throw new IOException( "Unsupported version: " + version); } } sampleFrequency = ((headerString >>> 10) & 3); if (sampleFrequency == 3) { throw new IOException( "Unsupported sampleFrequency: " + sampleFrequency); } } int layer = 4 - (headerString >>> 17) & 3; if (layer != 3) { throw new IOException("Unsupported layer: " + layer); } protectionBit = ((headerString >>> 16) & 1) != 0; bitrateIndex = (headerString >>> 12) & 0xF; paddingBit = ((headerString >>> 9) & 1) != 0; mode = ((headerString >>> 6) & 3); modeExtension = (headerString >>> 4) & 3; if (mode == MODE_JOINT_STEREO) { intensityStereoBound = (modeExtension << 2) + 4; } else { intensityStereoBound = 0; // should never be used } // calculate number of subbands: int channelBitrate = bitrateIndex; // calculate bitrate per channel: if (mode != MODE_SINGLE_CHANNEL) { if (channelBitrate == 4) { channelBitrate = 1; } else { channelBitrate -= 4; } } if (channelBitrate == 1 || channelBitrate == 2) { if (sampleFrequency == SAMPLE_FREQUENCY_THIRTYTWO) { numberOfSubbands = 12; } else { numberOfSubbands = 8; } } else if (sampleFrequency == SAMPLE_FREQUENCY_FOURTYEIGHT || (channelBitrate >= 3 && channelBitrate <= 5)) { numberOfSubbands = 27; } else { numberOfSubbands = 30; } if (intensityStereoBound > numberOfSubbands) { intensityStereoBound = numberOfSubbands; } calculateFramesize(); int frameSizeLoaded = stream.readFrameData(frameSize); if (frameSize >= 0 && frameSizeLoaded != frameSize) { // Data loaded does not match to expected framesize, // it might be an ID3v1 Tag return false; } if (stream.isSyncCurrentPosition(syncMode)) { if (syncMode == Bitstream.INITIAL_SYNC) { syncMode = Bitstream.STRICT_SYNC; stream.setSyncWord(headerString & 0xFFF80CC0); } break; } stream.unreadFrame(); } stream.parseFrame(); if (!protectionBit) { // frame contains a crc checksum stream.getBits(16); } return true; } void parseVBR(byte[] firstFrame) throws IOException { // trying Xing header byte[] tmp = new byte[4]; int offset; if (version == VERSION_MPEG1) { if (mode == MODE_SINGLE_CHANNEL) { offset = 21 - 4; } else { offset = 36 - 4; } } else { if (mode == MODE_SINGLE_CHANNEL) { offset = 13 - 4; } else { offset = 21 - 4; } } try { System.arraycopy(firstFrame, offset, tmp, 0, 4); if ("Xing".equals(new String(tmp, Charsets.UTF_8))) { vbr = true; } } catch (ArrayIndexOutOfBoundsException e) { throw new IOException("Corrupt Xing VBR header"); } offset = 36 - 4; try { System.arraycopy(firstFrame, offset, tmp, 0, 4); if ("VBRI".equals(new String(tmp, Charsets.UTF_8))) { vbr = true; } } catch (ArrayIndexOutOfBoundsException e) { throw new IOException("Corrupt VBRI VBR header"); } } int version() { return version; } int sampleFrequency() { return sampleFrequency; } public int frequency() { return FREQUENCIES[version][sampleFrequency]; } public int mode() { return mode; } int slots() { return slots; } boolean vbr() { return vbr; } int modeExtension() { return modeExtension; } private void calculateFramesize() { frameSize = (144 * Constants.BITRATES[version][bitrateIndex]) / frequency(); if (version == VERSION_MPEG2_LSF || version == VERSION_MPEG25_LSF) { frameSize >>= 1; } if (paddingBit) { frameSize++; } // subtract header size frameSize -= 4; // side info size, crc size, header sidze if (version == VERSION_MPEG1) { slots = (mode == MODE_SINGLE_CHANNEL) ? 17 : 32; } else { slots = (mode == MODE_SINGLE_CHANNEL) ? 9 : 17; } slots = frameSize - slots - (protectionBit ? 0 : 2); } }