All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jflac.frame.Header Maven / Gradle / Ivy

There is a newer version: 1.5.2
Show newest version
package org.jflac.frame;

/**
 * libFLAC - Free Lossless Audio Codec library
 * Copyright (C) 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 java.io.IOException;

import org.jflac.Constants;
import org.jflac.io.BitInputStream;
import org.jflac.metadata.StreamInfo;
import org.jflac.util.ByteData;
import org.jflac.util.CRC8;

/**
 * Frame header class.
 * @author kc7bfi
 */
public class Header {
    
    /** The number of samples per subframe. */
    public int blockSize;
    /** The sample rate in Hz. */
    public int sampleRate;
    /** The number of channels (== number of subframes). */
    public int channels;
    /** The channel assignment for the frame. */
    public int channelAssignment;
    /** The sample resolution. */
    public int bitsPerSample;
    
    /** 
     * The frame number or sample number of first sample in frame.
     * use the number_type value to determine which to use. 
     */
    public int frameNumber = -1;
    
    /**
     * The sample number for the first sample in the frame.
     */
    public long sampleNumber = -1;
    
    /** 
     * CRC-8 (polynomial = x^8 + x^2 + x^1 + x^0, initialized with 0).
     * of the raw frame header bytes, meaning everything before the CRC byte
     * including the sync code.
     */
    protected byte crc;
    
    /**
     * The constructor.
     * @param is                    The InputBitStream
     * @param headerWarmup          The header warm-up bytes
     * @param streamInfo            The FLAC Stream Info
     * @throws IOException          Thrown on error reading InputBitStream
     * @throws BadHeaderException   Thrown if header is bad
     */
    public Header(BitInputStream is, byte[] headerWarmup, StreamInfo streamInfo) throws IOException, BadHeaderException {
        int blocksizeHint = 0;
        int sampleRateHint = 0;
        ByteData rawHeader = new ByteData(16); // MAGIC NUMBER based on the maximum frame header size, including CRC
        
        // init the raw header with the saved bits from synchronization
        rawHeader.append(headerWarmup[0]);
        rawHeader.append(headerWarmup[1]);
        
        // check to make sure that the reserved bit is 0
        if ((rawHeader.getData(1) & 0x02) != 0) { // MAGIC NUMBER
            throw new BadHeaderException("Bad Magic Number: " + (rawHeader.getData(1) & 0xff));
        }
        
        // Note that along the way as we read the header, we look for a sync
        // code inside.  If we find one it would indicate that our original
        // sync was bad since there cannot be a sync code in a valid header.
        
        // read in the raw header as bytes so we can CRC it, and parse it on the way
        for (int i = 0; i < 2; i++) {
            if (is.peekRawUInt(8) == 0xff) { // MAGIC NUMBER for the first 8 frame sync bits
                throw new BadHeaderException("Found sync byte");
            }
            rawHeader.append((byte) is.readRawUInt(8));
        }
        
        int bsType = (rawHeader.getData(2) >> 4) & 0x0f;
        switch (bsType) {
            case 0 :                
                throw new BadHeaderException("Unknown Block Size (0)");                
            case 1 :
                blockSize = 192;
                break;
            case 2 :
            case 3 :
            case 4 :
            case 5 :
                blockSize = 576 << (bsType - 2);
                break;
            case 6 :
            case 7 :
                blocksizeHint = bsType;
                break;
            case 8 :
            case 9 :
            case 10 :
            case 11 :
            case 12 :
            case 13 :
            case 14 :
            case 15 :
                blockSize = 256 << (bsType - 8);
                break;
            default :
                break;
        }
        //System.out.println("BSType="+bsType+" BS="+blockSize);
        
        int srType = rawHeader.getData(2) & 0x0f;
        switch (srType) {
            case 0 :
                if (streamInfo == null)
                    throw new BadHeaderException("Bad Sample Rate (0)");
                sampleRate = streamInfo.getSampleRate();
                break;
            case 1 :
            	sampleRate = 88200;
                break;
            case 2 :
            	sampleRate = 176400;
                break;
            case 3 :
            	sampleRate = 192000;
                break;
            case 4 :
                sampleRate = 8000;
                break;
            case 5 :
                sampleRate = 16000;
                break;
            case 6 :
                sampleRate = 22050;
                break;
            case 7 :
                sampleRate = 24000;
                break;
            case 8 :
                sampleRate = 32000;
                break;
            case 9 :
                sampleRate = 44100;
                break;
            case 10 :
                sampleRate = 48000;
                break;
            case 11 :
                sampleRate = 96000;
                break;
            case 12 :
            case 13 :
            case 14 :
                sampleRateHint = srType;
                break;
            case 15 :
                throw new BadHeaderException("Bad Sample Rate (" + srType + ")");
            default :
        }
        
        int asgnType = (int) ((rawHeader.getData(3) >> 4) & 0x0f);
        //System.out.println("AsgnType="+asgnType+" "+(rawHeader.space[3] >> 4));
        if ((asgnType & 8) != 0) {
            channels = 2;
            switch (asgnType & 7) {
                case 0 :
                    channelAssignment = Constants.CHANNEL_ASSIGNMENT_LEFT_SIDE;
                    break;
                case 1 :
                    channelAssignment = Constants.CHANNEL_ASSIGNMENT_RIGHT_SIDE;
                    break;
                case 2 :
                    channelAssignment = Constants.CHANNEL_ASSIGNMENT_MID_SIDE;
                    break;
                default :
                    throw new BadHeaderException("Bad Channel Assignment (" + asgnType + ")");
            }
        } else {
            channels = (int) asgnType + 1;
            channelAssignment = Constants.CHANNEL_ASSIGNMENT_INDEPENDENT;
        }
        
        int bpsType = (int) (rawHeader.getData(3) & 0x0e) >> 1;
        switch (bpsType) {
            case 0 :
                if (streamInfo != null)
                    bitsPerSample = streamInfo.getBitsPerSample();
                else
                    throw new BadHeaderException("Bad BPS (" + bpsType + ")");
                break;
            case 1 :
                bitsPerSample = 8;
                break;
            case 2 :
                bitsPerSample = 12;
                break;
            case 4 :
                bitsPerSample = 16;
                break;
            case 5 :
                bitsPerSample = 20;
                break;
            case 6 :
                bitsPerSample = 24;
                break;
            case 3 :
            case 7 :
                throw new BadHeaderException("Bad BPS (" + bpsType + ")");
            default :
                break;
        }
        
        /* check to make sure that reserved bit is 0 */        
        if ((rawHeader.getData(3) & 0x01) != 0) { /* MAGIC NUMBER */
        	throw new BadHeaderException("Bad Magic Number: " + (rawHeader.getData(3) & 0x01));
        }
        
        /* read the frame's starting sample number (or frame number as the case may be) */
    	if(
    		(rawHeader.getData(1) & 0x01) != 0 ||
    		/*@@@ this clause is a concession to the old way of doing variable blocksize; the only known implementation is flake and can probably be removed without inconveniencing anyone */
    		(streamInfo != null && streamInfo.getMinBlockSize() != streamInfo.getMaxBlockSize())
    	) { /* variable blocksize */
            sampleNumber = is.readUTF8Long(rawHeader);
            if (sampleNumber == 0xffffffffffffffffL) { // i.e. non-UTF8 code...
                throw new BadHeaderException("Bad Sample Number");
            }
    	}
    	else
    	{ /* fixed blocksize */
            int lastFrameNumber = is.readUTF8Int(rawHeader);
            if (lastFrameNumber == 0xffffffff) { // i.e. non-UTF8 code...
                throw new BadHeaderException("Bad Last Frame");
            }
            sampleNumber = (long) streamInfo.getMinBlockSize() * (long) lastFrameNumber;
    	}
        
        if (blocksizeHint != 0) {
            int blockSizeCode = is.readRawUInt(8);
            rawHeader.append((byte) blockSizeCode);
            if (blocksizeHint == 7) {
                int blockSizeCode2 = is.readRawUInt(8);
                rawHeader.append((byte) blockSizeCode2);
                blockSizeCode = (blockSizeCode << 8) | blockSizeCode2;
            }
            blockSize = blockSizeCode + 1;
        }
        
        if (sampleRateHint != 0) {
            int sampleRateCode = is.readRawUInt(8);
            rawHeader.append((byte) sampleRateCode);
            if (sampleRateHint != 12) {
                int sampleRateCode2 = is.readRawUInt(8);
                rawHeader.append((byte) sampleRateCode2);
                sampleRateCode = (sampleRateCode << 8) | sampleRateCode2;
            }
            if (sampleRateHint == 12)
                sampleRate = sampleRateCode * 1000;
            else if (sampleRateHint == 13)
                sampleRate = sampleRateCode;
            else
                sampleRate = sampleRateCode * 10;
        }
        
        // read the CRC-8 byte
        byte crc8 = (byte) is.readRawUInt(8);
        
        if (CRC8.calc(rawHeader.getData(), rawHeader.getLen()) != crc8) {
            throw new BadHeaderException("STREAM_DECODER_ERROR_STATUS_BAD_HEADER");
        }
    }
    
    /**
     * Return a descriptive string for this object.
     * @return the string description
     * @see java.lang.Object#toString()
     */
    public String toString() {
        return "FrameHeader:"
            + " BlockSize=" + blockSize
            + " SampleRate=" + sampleRate
            + " Channels=" + channels
            + " ChannelAssignment=" + channelAssignment
            + " BPS=" + bitsPerSample
            + " SampleNumber=" + sampleNumber;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy