package org.kc7bfi.jflac;
/**
* libFLAC - Free Lossless Audio Codec library Copyright (C) 2000,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.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Vector;
import org.kc7bfi.jflac.frame.BadHeaderException;
import org.kc7bfi.jflac.frame.ChannelConstant;
import org.kc7bfi.jflac.frame.ChannelFixed;
import org.kc7bfi.jflac.frame.ChannelLPC;
import org.kc7bfi.jflac.frame.ChannelVerbatim;
import org.kc7bfi.jflac.frame.Frame;
import org.kc7bfi.jflac.frame.Header;
import org.kc7bfi.jflac.io.BitInputStream;
import org.kc7bfi.jflac.io.RandomFileInputStream;
import org.kc7bfi.jflac.metadata.Application;
import org.kc7bfi.jflac.metadata.CueSheet;
import org.kc7bfi.jflac.metadata.Metadata;
import org.kc7bfi.jflac.metadata.Padding;
import org.kc7bfi.jflac.metadata.Picture;
import org.kc7bfi.jflac.metadata.SeekPoint;
import org.kc7bfi.jflac.metadata.SeekTable;
import org.kc7bfi.jflac.metadata.StreamInfo;
import org.kc7bfi.jflac.metadata.Unknown;
import org.kc7bfi.jflac.metadata.VorbisComment;
import org.kc7bfi.jflac.util.ByteData;
import org.kc7bfi.jflac.util.CRC16;
/**
* A Java FLAC decoder.
*
* @author kc7bfi
*/
public class FLACDecoder {
private static final int FRAME_FOOTER_CRC_LEN = 16; // bits
private static final byte[] ID3V2_TAG = new byte[]{'I', 'D', '3'};
private BitInputStream bitStream;
private ChannelData[] channelData = new ChannelData[Constants.MAX_CHANNELS];
private int outputCapacity = 0;
private int outputChannels = 0;
private int lastFrameNumber;
private long samplesDecoded = 0;
private StreamInfo streamInfo;
private Frame frame = new Frame();
private byte[] headerWarmup = new byte[2]; // contains the sync code and reserved bits
//private int state;
private int channels;
private int channelAssignment;
private int bitsPerSample;
private int sampleRate; // in Hz
private int blockSize; // in samples (per channel)
private InputStream inputStream = null;
private int badFrames;
private boolean eof = false;
private FrameListeners frameListeners = new FrameListeners();
private PCMProcessors pcmProcessors = new PCMProcessors();
// Decoder states
//private static final int DECODER_SEARCH_FOR_METADATA = 0;
//private static final int DECODER_READ_METADATA = 1;
//private static final int DECODER_SEARCH_FOR_FRAME_SYNC = 2;
//private static final int DECODER_READ_FRAME = 3;
//private static final int DECODER_END_OF_STREAM = 4;
//private static final int DECODER_ABORTED = 5;
//private static final int DECODER_UNPARSEABLE_STREAM = 6;
//private static final int STREAM_DECODER_MEMORY_ALLOCATION_ERROR = 7;
//private static final int STREAM_DECODER_ALREADY_INITIALIZED = 8;
//private static final int STREAM_DECODER_INVALID_CALLBACK = 9;
//private static final int STREAM_DECODER_UNINITIALIZED = 10;
/**
* The constructor.
*
* @param inputStream The input stream to read data from
*/
public FLACDecoder(InputStream inputStream) {
this.inputStream = inputStream;
this.bitStream = new BitInputStream(inputStream);
//state = DECODER_SEARCH_FOR_METADATA;
lastFrameNumber = 0;
samplesDecoded = 0;
//state = DECODER_SEARCH_FOR_METADATA;
}
/**
* Return the parsed StreamInfo Metadata record.
*
* @return The StreamInfo
*/
public StreamInfo getStreamInfo() {
return streamInfo;
}
/**
* Return the ChannelData object.
*
* @return The ChannelData object
*/
public ChannelData[] getChannelData() {
return channelData;
}
/**
* Return the input but stream.
*
* @return The bit stream
*/
public BitInputStream getBitInputStream() {
return bitStream;
}
/**
* Add a frame listener.
*
* @param listener The frame listener to add
*/
public void addFrameListener(FrameListener listener) {
frameListeners.addFrameListener(listener);
}
/**
* Remove a frame listener.
*
* @param listener The frame listener to remove
*/
public void removeFrameListener(FrameListener listener) {
frameListeners.removeFrameListener(listener);
}
/**
* Add a PCM processor.
*
* @param processor The processor listener to add
*/
public void addPCMProcessor(PCMProcessor processor) {
pcmProcessors.addPCMProcessor(processor);
}
/**
* Remove a PCM processor.
*
* @param processor The processor listener to remove
*/
public void removePCMProcessor(PCMProcessor processor) {
pcmProcessors.removePCMProcessor(processor);
}
private void callPCMProcessors(Frame frame) {
ByteData bd = decodeFrame(frame, null);
pcmProcessors.processPCM(bd);
}
/**
* Fill the given ByteData object with PCM data from the frame.
*
* @param frame the frame to send to the PCM processors
* @param pcmData the byte data to be filled, or null if it should be
* allocated
* @return the ByteData that was filled (may be a new instance * from <code>space</code>)
*/
public ByteData decodeFrame(Frame frame, ByteData pcmData) {
// required size of the byte buffer
int byteSize = frame.header.blockSize * channels * ( ( streamInfo.getBitsPerSample() + 7 ) / 2 );
if (pcmData == null || pcmData.getData().length < byteSize) {
pcmData = new ByteData(byteSize);
} else {
pcmData.setLen(0);
}
if (streamInfo.getBitsPerSample() == 8) {
for (int i = 0; i < frame.header.blockSize; i++) {
for (int channel = 0; channel < channels; channel++) {
pcmData.append((byte) ( channelData[channel].getOutput()[i] + 0x80 ));
}
}
} else if (streamInfo.getBitsPerSample() == 16) {
for (int i = 0; i < frame.header.blockSize; i++) {
for (int channel = 0; channel < channels; channel++) {
short val = (short) ( channelData[channel].getOutput()[i] );
pcmData.append((byte) ( val & 0xff ));
pcmData.append((byte) ( ( val >> 8 ) & 0xff ));
}
}
} else if (streamInfo.getBitsPerSample() == 24) {
for (int i = 0; i < frame.header.blockSize; i++) {
for (int channel = 0; channel < channels; channel++) {
int val = ( channelData[channel].getOutput()[i] );
pcmData.append((byte) ( val & 0xff ));
pcmData.append((byte) ( ( val >> 8 ) & 0xff ));
pcmData.append((byte) ( ( val >> 16 ) & 0xff ));
}
}
}
return pcmData;
}
/**
* Read the FLAC stream info.
*
* @return The FLAC Stream Info record
* @throws IOException On read error
*/
public StreamInfo readStreamInfo() throws IOException {
readStreamSync();
Metadata metadata = readNextMetadata();
if (!( metadata instanceof StreamInfo )) {
throw new IOException("StreamInfo metadata block missing");
}
return (StreamInfo) metadata;
}
/**
* Read an array of metadata blocks.
*
* @return The array of metadata blocks
* @throws IOException On read error
*/
public Metadata[] readMetadata() throws IOException {
readStreamSync();
Vector metadataList = new Vector();
Metadata metadata;
do {
metadata = readNextMetadata();
metadataList.add(metadata);
} while (!metadata.isLast());
return (Metadata[]) metadataList.toArray(new Metadata[0]);
}
/**
* Read an array of metadata blocks.
*
* @param streamInfo The StreamInfo metadata block previously read
* @return The array of metadata blocks
* @throws IOException On read error
*/
public Metadata[] readMetadata(StreamInfo streamInfo) throws IOException {
if (streamInfo.isLast()) {
return new Metadata[0];
}
Vector metadataList = new Vector();
Metadata metadata;
do {
metadata = readNextMetadata();
metadataList.add(metadata);
} while (!metadata.isLast());
return (Metadata[]) metadataList.toArray(new Metadata[0]);
}
/**
* process a single metadata/frame.
*
* @return True of one processed
* @throws IOException on read error
*/
/*
public boolean processSingle() throws IOException {
while (true) {
switch (state) {
case DECODER_SEARCH_FOR_METADATA :
readStreamSync();
break;
case DECODER_READ_METADATA :
readNextMetadata(); // above function sets the status for us
return true;
case DECODER_SEARCH_FOR_FRAME_SYNC :
frameSync(); // above function sets the status for us
break;
case DECODER_READ_FRAME :
readFrame();
return true; // above function sets the status for us
//break;
case DECODER_END_OF_STREAM :
case DECODER_ABORTED :
return true;
default :
return false;
}
}
}
*/
/**
* Process all the metadata records.
*
* @throws IOException On read error
*/
/*
public void processMetadata() throws IOException {
while (true) {
switch (state) {
case DECODER_SEARCH_FOR_METADATA :
readStreamSync();
break;
case DECODER_READ_METADATA :
readNextMetadata(); // above function sets the status for us
break;
case DECODER_SEARCH_FOR_FRAME_SYNC :
case DECODER_READ_FRAME :
case DECODER_END_OF_STREAM :
case DECODER_ABORTED :
default :
return;
}
}
}
*/
/**
* Decode the FLAC file.
*
* @throws IOException On read error
*/
public void decode() throws IOException {
readMetadata();
try {
while (true) {
//switch (state) {
//case DECODER_SEARCH_FOR_METADATA :
// readStreamSync();
// break;
//case DECODER_READ_METADATA :
// Metadata metadata = readNextMetadata();
// if (metadata == null) break;
// break;
//case DECODER_SEARCH_FOR_FRAME_SYNC :
findFrameSync();
// break;
//case DECODER_READ_FRAME :
try {
readFrame();
frameListeners.processFrame(frame);
callPCMProcessors(frame);
}
catch (FrameDecodeException e) {
badFrames++;
}
// break;
//case DECODER_END_OF_STREAM :
//case DECODER_ABORTED :
// return;
//default :
// throw new IOException("Unknown state: " + state);
//}
}
}
catch (EOFException e) {
eof = true;
}
}
/**
* Decode the data frames.
*
* @throws IOException On read error
*/
public void decodeFrames() throws IOException {
//state = DECODER_SEARCH_FOR_FRAME_SYNC;
try {
while (true) {
//switch (state) {
//case DECODER_SEARCH_FOR_METADATA :
// readStreamSync();
// break;
//case DECODER_READ_METADATA :
// Metadata metadata = readNextMetadata();
// if (metadata == null) break;
// break;
//case DECODER_SEARCH_FOR_FRAME_SYNC :
findFrameSync();
// break;
//case DECODER_READ_FRAME :
try {
readFrame();
frameListeners.processFrame(frame);
callPCMProcessors(frame);
}
catch (FrameDecodeException e) {
badFrames++;
}
// break;
//case DECODER_END_OF_STREAM :
//case DECODER_ABORTED :
// return;
//default :
// throw new IOException("Unknown state: " + state);
//}
}
}
catch (EOFException e) {
eof = true;
}
}
/**
* Decode the data frames between two seek points.
*
* @param from The starting seek point
* @param to The ending seek point (non-inclusive)
* @throws IOException On read error
*/
public void decode(SeekPoint from, SeekPoint to) throws IOException {
// position random access file
if (!( inputStream instanceof RandomFileInputStream )) {
throw new IOException("Not a RandomFileInputStream: " + inputStream.getClass().getName());
}
( (RandomFileInputStream) inputStream ).seek(from.getStreamOffset());
bitStream.reset();
samplesDecoded = from.getSampleNumber();
//state = DECODER_SEARCH_FOR_FRAME_SYNC;
try {
while (true) {
//switch (state) {
//case DECODER_SEARCH_FOR_METADATA :
// readStreamSync();
// break;
//case DECODER_READ_METADATA :
// Metadata metadata = readNextMetadata();
// if (metadata == null) break;
// break;
//case DECODER_SEARCH_FOR_FRAME_SYNC :
findFrameSync();
// break;
//case DECODER_READ_FRAME :
try {
readFrame();
frameListeners.processFrame(frame);
callPCMProcessors(frame);
}
catch (FrameDecodeException e) {
badFrames++;
}
//frameListeners.processFrame(frame);
//callPCMProcessors(frame);
//System.out.println(samplesDecoded +" "+ to.getSampleNumber());
if (to != null && samplesDecoded >= to.getSampleNumber()) {
return;
}
// break;
//case DECODER_END_OF_STREAM :
//case DECODER_ABORTED :
// return;
//default :
// throw new IOException("Unknown state: " + state);
//}
}
}
catch (EOFException e) {
eof = true;
}
}
/*
private boolean processUntilEndOfStream() throws IOException {
//boolean got_a_frame;
while (true) {
switch (state) {
case DECODER_SEARCH_FOR_METADATA :
readStreamSync();
break;
case DECODER_READ_METADATA :
readNextMetadata(); // above function sets the status for us
break;
case DECODER_SEARCH_FOR_FRAME_SYNC :
frameSync(); // above function sets the status for us
//System.exit(0);
break;
case DECODER_READ_FRAME :
readFrame();
break;
case DECODER_END_OF_STREAM :
case DECODER_ABORTED :
return true;
default :
return false;
}
}
}
*/
/**
* Read the next data frame.
*
* @return The next frame
* @throws IOException on read error
*/
public Frame readNextFrame() throws IOException {
//boolean got_a_frame;
try {
while (true) {
//switch (state) {
//case STREAM_DECODER_SEARCH_FOR_METADATA :
// findMetadata();
// break;
//case STREAM_DECODER_READ_METADATA :
// readMetadata(); /* above function sets the status for us */
// break;
//case DECODER_SEARCH_FOR_FRAME_SYNC :
findFrameSync(); /* above function sets the status for us */
//System.exit(0);
//break;
//case DECODER_READ_FRAME :
try {
readFrame();
return frame;
}
catch (FrameDecodeException e) {
badFrames++;
}
//break;
//case DECODER_END_OF_STREAM :
//case DECODER_ABORTED :
// return null;
//default :
// return null;
//}
}
}
catch (EOFException e) {
eof = true;
}
return null;
}
/**
* Bytes consumed.
*
* @return The number of bytes read
*/
//public long getBytesConsumed() {
// return is.getConsumedBlurbs();
//}
/**
* Bytes read.
*
* @return The number of bytes read
*/
public long getTotalBytesRead() {
return bitStream.getTotalBytesRead();
}
/*
public int getInputBytesUnconsumed() {
return is.getInputBytesUnconsumed();
}
*/
private void allocateOutput(int size, int channels) {
if (size <= outputCapacity && channels <= outputChannels) {
return;
}
for (int i = 0; i < Constants.MAX_CHANNELS; i++) {
channelData[i] = null;
}
for (int i = 0; i < channels; i++) {
channelData[i] = new ChannelData(size);
}
outputCapacity = size;
outputChannels = channels;
}
/**
* Read the stream sync string.
*
* @throws IOException On read error
*/
private void readStreamSync() throws IOException {
int id = 0;
for (int i = 0; i < 4;) {
int x = bitStream.readRawUInt(8);
if (x == Constants.STREAM_SYNC_STRING[i]) {
i++;
id = 0;
} else if (x == ID3V2_TAG[id]) {
id++;
i = 0;
if (id == 3) {
skipID3v2Tag();
id = 0;
}
} else {
throw new IOException("Could not find Stream Sync");
//i = 0;
//id = 0;
}
}
}
/**
* Read a single metadata record.
*
* @return The next metadata record
* @throws IOException on read error
*/
public Metadata readNextMetadata() throws IOException {
Metadata metadata = null;
boolean isLast = ( bitStream.readRawUInt(Metadata.STREAM_METADATA_IS_LAST_LEN) != 0 );
int type = bitStream.readRawUInt(Metadata.STREAM_METADATA_TYPE_LEN);
int length = bitStream.readRawUInt(Metadata.STREAM_METADATA_LENGTH_LEN);
if (type == Metadata.METADATA_TYPE_STREAMINFO) {
streamInfo = new StreamInfo(bitStream, length, isLast);
metadata = streamInfo;
pcmProcessors.processStreamInfo((StreamInfo) metadata);
} else if (type == Metadata.METADATA_TYPE_SEEKTABLE) {
metadata = new SeekTable(bitStream, length, isLast);
} else if (type == Metadata.METADATA_TYPE_APPLICATION) {
metadata = new Application(bitStream, length, isLast);
} else if (type == Metadata.METADATA_TYPE_PADDING) {
metadata = new Padding(bitStream, length, isLast);
} else if (type == Metadata.METADATA_TYPE_VORBIS_COMMENT) {
metadata = new VorbisComment(bitStream, length, isLast);
} else if (type == Metadata.METADATA_TYPE_CUESHEET) {
metadata = new CueSheet(bitStream, length, isLast);
} else if (type == Metadata.METADATA_TYPE_PICTURE) {
metadata = new Picture(bitStream, length, isLast);
} else {
metadata = new Unknown(bitStream, length, isLast);
}
frameListeners.processMetadata(metadata);
//if (isLast) state = DECODER_SEARCH_FOR_FRAME_SYNC;
return metadata;
}
private void skipID3v2Tag() throws IOException {
// skip the version and flags bytes
int verMajor = bitStream.readRawInt(8);
int verMinor = bitStream.readRawInt(8);
int flags = bitStream.readRawInt(8);
// get the size (in bytes) to skip
int skip = 0;
for (int i = 0; i < 4; i++) {
int x = bitStream.readRawUInt(8);
skip <<= 7;
skip |= ( x & 0x7f );
}
// skip the rest of the tag
bitStream.readByteBlockAlignedNoCRC(null, skip);
}
private void findFrameSync() throws IOException {
boolean first = true;
//int cnt=0;
// If we know the total number of samples in the stream, stop if we've read that many.
// This will stop us, for example, from wasting time trying to sync on an ID3V1 tag.
if (streamInfo != null && ( streamInfo.getTotalSamples() != 0 )) {
if (samplesDecoded >= streamInfo.getTotalSamples()) {
//state = DECODER_END_OF_STREAM;
return;
}
}
// make sure we're byte aligned
if (!bitStream.isConsumedByteAligned()) {
bitStream.readRawUInt(bitStream.bitsLeftForByteAlignment());
}
int x;
try {
while (true) {
x = bitStream.readRawUInt(8);
if (x == 0xff) { // MAGIC NUMBER for the first 8 frame sync bits
headerWarmup[0] = (byte) x;
x = bitStream.peekRawUInt(8);
/* we have to check if we just read two 0xff's in a row; the second may actually be the beginning of the sync code */
/* else we have to check if the second byte is the end of a sync code */
if (x >> 2 == 0x3e) { /* MAGIC NUMBER for the last 6 sync bits */
headerWarmup[1] = (byte) bitStream.readRawUInt(8);
//state = DECODER_READ_FRAME;
return;
}
}
if (first) {
frameListeners.processError("FindSync LOST_SYNC: " + Integer.toHexString(( x & 0xff )));
first = false;
}
}
}
catch (EOFException e) {
if (!first) {
frameListeners.processError("FindSync LOST_SYNC: Left over data in file");
}
//state = DECODER_END_OF_STREAM;
}
}
/**
* Read the next data frame.
*
* @throws IOException On read error
* @throws FrameDecodeException On frame decoding error
*/
public void readFrame() throws IOException, FrameDecodeException {
boolean gotAFrame = false;
int channel;
int i;
int mid, side, left, right;
short frameCRC; /* the one we calculate from the input stream */
//int x;
/* init the CRC */
frameCRC = 0;
frameCRC = CRC16.update(headerWarmup[0], frameCRC);
frameCRC = CRC16.update(headerWarmup[1], frameCRC);
bitStream.resetReadCRC16(frameCRC);
try {
frame.header = new Header(bitStream, headerWarmup, streamInfo);
}
catch (BadHeaderException e) {
frameListeners.processError("Found bad header: " + e);
throw new FrameDecodeException("Bad Frame Header: " + e);
}
//if (state == DECODER_SEARCH_FOR_FRAME_SYNC) return false;
allocateOutput(frame.header.blockSize, frame.header.channels);
for (channel = 0; channel < frame.header.channels; channel++) {
// first figure the correct bits-per-sample of the subframe
int bps = frame.header.bitsPerSample;
switch (frame.header.channelAssignment) {
case Constants.CHANNEL_ASSIGNMENT_INDEPENDENT:
/* no adjustment needed */
break;
case Constants.CHANNEL_ASSIGNMENT_LEFT_SIDE:
if (channel == 1) {
bps++;
}
break;
case Constants.CHANNEL_ASSIGNMENT_RIGHT_SIDE:
if (channel == 0) {
bps++;
}
break;
case Constants.CHANNEL_ASSIGNMENT_MID_SIDE:
if (channel == 1) {
bps++;
}
break;
default:
}
// now read it
try {
readSubframe(channel, bps);
}
catch (IOException e) {
frameListeners.processError("ReadSubframe: " + e);
throw e;
}
}
readZeroPadding();
// Read the frame CRC-16 from the footer and check
frameCRC = bitStream.getReadCRC16();
frame.setCRC((short) bitStream.readRawUInt(FRAME_FOOTER_CRC_LEN));
if (frameCRC == frame.getCRC()) {
/* Undo any special channel coding */
switch (frame.header.channelAssignment) {
case Constants.CHANNEL_ASSIGNMENT_INDEPENDENT:
/* do nothing */
break;
case Constants.CHANNEL_ASSIGNMENT_LEFT_SIDE:
for (i = 0; i < frame.header.blockSize; i++) {
channelData[1].getOutput()[i] = channelData[0].getOutput()[i] - channelData[1].getOutput()[i];
}
break;
case Constants.CHANNEL_ASSIGNMENT_RIGHT_SIDE:
for (i = 0; i < frame.header.blockSize; i++) {
channelData[0].getOutput()[i] += channelData[1].getOutput()[i];
}
break;
case Constants.CHANNEL_ASSIGNMENT_MID_SIDE:
for (i = 0; i < frame.header.blockSize; i++) {
mid = channelData[0].getOutput()[i];
side = channelData[1].getOutput()[i];
mid <<= 1;
if (( side & 1 ) != 0) // i.e. if 'side' is odd...
{
mid++;
}
left = mid + side;
right = mid - side;
channelData[0].getOutput()[i] = left >> 1;
channelData[1].getOutput()[i] = right >> 1;
}
//System.exit(1);
break;
default:
break;
}
gotAFrame = true;
} else {
// Bad frame, emit error and zero the output signal
frameListeners.processError("CRC Error: " + Integer.toHexString(( frameCRC & 0xffff )) + " vs " + Integer.toHexString(( frame.getCRC() & 0xffff )));
for (channel = 0; channel < frame.header.channels; channel++) {
for (int j = 0; j < frame.header.blockSize; j++) {
channelData[channel].getOutput()[j] = 0;
}
}
}
// put the latest values into the public section of the decoder instance
channels = frame.header.channels;
channelAssignment = frame.header.channelAssignment;
bitsPerSample = frame.header.bitsPerSample;
sampleRate = frame.header.sampleRate;
blockSize = frame.header.blockSize;
//samplesDecoded = frame.header.sampleNumber + frame.header.blockSize;
samplesDecoded += frame.header.blockSize;
//System.out.println(samplesDecoded+" "+frame.header.sampleNumber + " "+frame.header.blockSize);
//state = DECODER_SEARCH_FOR_FRAME_SYNC;
//return;
}
private void readSubframe(int channel, int bps) throws IOException, FrameDecodeException {
int x;
x = bitStream.readRawUInt(8); /* MAGIC NUMBER */
boolean haveWastedBits = ( ( x & 1 ) != 0 );
x &= 0xfe;
int wastedBits = 0;
if (haveWastedBits) {
wastedBits = bitStream.readUnaryUnsigned() + 1;
bps -= wastedBits;
}
// Lots of magic numbers here
if (( x & 0x80 ) != 0) {
frameListeners.processError("ReadSubframe LOST_SYNC: " + Integer.toHexString(x & 0xff));
//state = DECODER_SEARCH_FOR_FRAME_SYNC;
throw new FrameDecodeException("ReadSubframe LOST_SYNC: " + Integer.toHexString(x & 0xff));
//return true;
} else if (x == 0) {
frame.subframes[channel] = new ChannelConstant(bitStream, frame.header, channelData[channel], bps, wastedBits);
} else if (x == 2) {
frame.subframes[channel] = new ChannelVerbatim(bitStream, frame.header, channelData[channel], bps, wastedBits);
} else if (x < 16) {
//state = DECODER_UNPARSEABLE_STREAM;
throw new FrameDecodeException("ReadSubframe Bad Subframe Type: " + Integer.toHexString(x & 0xff));
} else if (x <= 24) {
//FLACSubframe_Fixed subframe = read_subframe_fixed_(channel, bps, (x >> 1) & 7);
frame.subframes[channel] = new ChannelFixed(bitStream, frame.header, channelData[channel], bps, wastedBits, ( x >> 1 ) & 7);
} else if (x < 64) {
//state = DECODER_UNPARSEABLE_STREAM;
throw new FrameDecodeException("ReadSubframe Bad Subframe Type: " + Integer.toHexString(x & 0xff));
} else {
frame.subframes[channel] = new ChannelLPC(bitStream, frame.header, channelData[channel], bps, wastedBits, ( ( x >> 1 ) & 31 ) + 1);
}
if (haveWastedBits) {
int i;
x = frame.subframes[channel].getWastedBits();
for (i = 0; i < frame.header.blockSize; i++) {
channelData[channel].getOutput()[i] <<= x;
}
}
}
private void readZeroPadding() throws IOException, FrameDecodeException {
if (!bitStream.isConsumedByteAligned()) {
int zero = bitStream.readRawUInt(bitStream.bitsLeftForByteAlignment());
if (zero != 0) {
frameListeners.processError("ZeroPaddingError: " + Integer.toHexString(zero));
//state = DECODER_SEARCH_FOR_FRAME_SYNC;
throw new FrameDecodeException("ZeroPaddingError: " + Integer.toHexString(zero));
}
}
}
/**
* Get the number of samples decoded.
*
* @return Returns the samples Decoded.
*/
public long getSamplesDecoded() {
return samplesDecoded;
}
/**
* @return Returns the number of bad frames decoded.
*/
public int getBadFrames() {
return badFrames;
}
/**
* @return Returns true if end-of-file.
*/
public boolean isEOF() {
return eof;
}
}