* @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
boolean isKnownVariableBlockSizeStream = ( streamInfo != null && streamInfo.getMinBlockSize() != streamInfo.getMaxBlockSize() );
boolean isKnownFixedBlockSizeStream = ( streamInfo != null && streamInfo.getMinBlockSize() == streamInfo.getMaxBlockSize() );
// 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 bits are 0
if (( rawHeader.getData(1) & 0x03 ) != 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:
if (!isKnownFixedBlockSizeStream) {
throw new BadHeaderException("Unknown Block Size (0)");
}
blockSize = streamInfo.getMinBlockSize();
break;
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:
case 2:
case 3:
throw new BadHeaderException("Bad Sample Rate (" + srType + ")");
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;
}
if (( rawHeader.getData(3) & 0x01 ) != 0) { // this should be a zero padding bit
throw new BadHeaderException("this should be a zero padding bit");
}
if (( blocksizeHint != 0 ) && isKnownVariableBlockSizeStream) {
sampleNumber = is.readUTF8Long(rawHeader);
if (sampleNumber == 0xffffffffffffffffL) { // i.e. non-UTF8 code...
throw new BadHeaderException("Bad Sample Number");
}
} else {
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");
}
}