FileDocCategorySizeDatePackage
MetadataBlockDataStreamInfo.javaAPI DocJaudiotagger 2.0.46861Wed Jun 08 11:59:18 BST 2011org.jaudiotagger.audio.flac.metadatablock

MetadataBlockDataStreamInfo.java

/*
 * Entagged Audio Tag library
 * Copyright (c) 2003-2005 Raphaƫl Slinckx <raphael@slinckx.net>
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 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
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
package org.jaudiotagger.audio.flac.metadatablock;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.util.logging.Logger;

/**
 * Stream Info
 * <p/>
 * <p>This block has information about the whole stream, like sample rate, number of channels, total number of samples,
 * etc. It must be present as the first metadata block in the stream. Other metadata blocks may follow, and ones
 * that the decoder doesn't understand, it will skip.
 * Format:
 * <Size in bits> Info
 * <16> The minimum block size (in samples) used in the stream.
 * <16> The maximum block size (in samples) used in the stream. (Minimum blocksize == maximum blocksize) implies a fixed-blocksize stream.
 * <24> The minimum frame size (in bytes) used in the stream. May be 0 to imply the value is not known.
 * <24> The maximum frame size (in bytes) used in the stream. May be 0 to imply the value is not known.
 * <20> Sample rate in Hz. Though 20 bits are available, the maximum sample rate is limited by the structure of frame headers to 655350Hz. Also,
 * a value of 0 is invalid.
 * <3> 	(number of channels)-1. FLAC supports from 1 to 8 channels
 * <5> 	(bits per sample)-1. FLAC supports from 4 to 32 bits per sample. Currently the reference encoder and decoders only support up to 24 bits per sample.
 * <36> 	Total samples in stream. 'Samples' means inter-channel sample,
 * i.e. one second of 44.1Khz audio will have 44100 samples regardless of the number of channels.
 * A value of zero here means the number of total samples is unknown.
 * <128> 	MD5 signature of the unencoded audio data. This allows the decoder to determine if an error exists in the audio data
 * even when the error does not result in an invalid bitstream.
 * NOTES
 * * FLAC specifies a minimum block size of 16 and a maximum block size of 65535, meaning the bit patterns corresponding to the numbers 0-15 in the minimum blocksize and maximum blocksize fields are invalid.
 */
public class MetadataBlockDataStreamInfo  implements MetadataBlockData
{
    public static final int STREAM_INFO_DATA_LENGTH = 34;

    // Logger Object
    public static Logger logger = Logger.getLogger("org.jaudiotagger.audio.flac.MetadataBlockDataStreamInfo");

    private int minBlockSize, maxBlockSize, minFrameSize, maxFrameSize, samplingRate, samplingRatePerChannel, bitsPerSample, channelNumber, totalNumberOfSamples;
    private float songLength;
    private boolean isValid = true;

    private ByteBuffer rawdata;

    public MetadataBlockDataStreamInfo(MetadataBlockHeader header, RandomAccessFile raf) throws IOException
    {
        rawdata = ByteBuffer.allocate(header.getDataLength());
        int bytesRead = raf.getChannel().read(rawdata);
        if (bytesRead < header.getDataLength())
        {
            throw new IOException("Unable to read required number of databytes read:" + bytesRead + ":required:" + header.getDataLength());
        }
        rawdata.rewind();

        minBlockSize = rawdata.getShort();
        maxBlockSize = rawdata.getShort();
        minFrameSize = readThreeByteInteger(rawdata.get(), rawdata.get(), rawdata.get());
        maxFrameSize = readThreeByteInteger(rawdata.get(), rawdata.get(), rawdata.get());

        samplingRate = readSamplingRate(rawdata.get(), rawdata.get(), rawdata.get());
        channelNumber = ((u(rawdata.get(12)) & 0x0E) >>> 1) + 1;
        samplingRatePerChannel = samplingRate / channelNumber;
        bitsPerSample = ((u(rawdata.get(12)) & 0x01) << 4) + ((u(rawdata.get(13)) & 0xF0) >>> 4) + 1;

        totalNumberOfSamples = readTotalNumberOfSamples(rawdata.get(13), rawdata.get(14), rawdata.get(15), rawdata.get(16), rawdata.get(17));

        songLength = (float) ((double) totalNumberOfSamples / samplingRate);
        logger.config(this.toString());
    }

    /**
     * @return the rawdata as it will be written to file
     */
    public byte[] getBytes()
    {
        return rawdata.array();
    }

    public int getLength()
    {
        return getBytes().length;
    }


    public String toString()
    {

        return "MinBlockSize:" + minBlockSize + "MaxBlockSize:" + maxBlockSize + "MinFrameSize:" + minFrameSize + "MaxFrameSize:" + maxFrameSize + "SampleRateTotal:" + samplingRate + "SampleRatePerChannel:" + samplingRatePerChannel + ":Channel number:" + channelNumber + ":Bits per sample: " + bitsPerSample + ":TotalNumberOfSamples: " + totalNumberOfSamples + ":Length: " + songLength;

    }

    public int getSongLength()
    {
        return (int) songLength;
    }

    public float getPreciseLength()
    {
        return songLength;
    }

    public int getChannelNumber()
    {
        return channelNumber;
    }

    public int getSamplingRate()
    {
        return samplingRate;
    }

    public int getSamplingRatePerChannel()
    {
        return samplingRatePerChannel;
    }

    public String getEncodingType()
    {
        return "FLAC " + bitsPerSample + " bits";
    }

    public boolean isValid()
    {
        return isValid;
    }

    private int readThreeByteInteger(byte b1, byte b2, byte b3)
    {
        int rate = (u(b1) << 16) + (u(b2) << 8) + (u(b3));
        return rate;
    }

    //TODO this code seems to be give a sampling rate over 21 bytes instead of 20 bytes but attempt to change
    //to 21 bytes give wrong value
    private int readSamplingRate(byte b1, byte b2, byte b3)
    {
        int rate = (u(b1) << 12) + (u(b2) << 4) + ((u(b3) & 0xF0) >>> 4);
        return rate;

    }

    private int readTotalNumberOfSamples(byte b1, byte b2, byte b3, byte b4, byte b5)
    {
        int nb = u(b5);
        nb += u(b4) << 8;
        nb += u(b3) << 16;
        nb += u(b2) << 24;
        nb += (u(b1) & 0x0F) << 32;
        return nb;
    }

    private int u(int i)
    {
        return i & 0xFF;
    }
}