/*
* myLib - https://github.com/taktod/myLib
* Copyright (c) 2014 ttProject. All rights reserved.
*
* Licensed under The MIT license.
*/
package com.ttProject.frame.mp3.type;
import java.nio.ByteBuffer;
import org.apache.log4j.Logger;
import com.ttProject.frame.mp3.Mp3Frame;
import com.ttProject.nio.channels.IReadChannel;
import com.ttProject.unit.extra.BitConnector;
import com.ttProject.unit.extra.BitLoader;
import com.ttProject.unit.extra.bit.Bit11;
import com.ttProject.unit.extra.bit.Bit1;
import com.ttProject.unit.extra.bit.Bit2;
import com.ttProject.unit.extra.bit.Bit3;
import com.ttProject.unit.extra.bit.Bit4;
import com.ttProject.util.BufferUtil;
/**
* mp3 frame.
* @author taktod
*/
public class Frame extends Mp3Frame {
/** logger */
@SuppressWarnings("unused")
private Logger logger = Logger.getLogger(Frame.class);
private Bit11 syncBit = new Bit11();
private Bit2 mpegVersion = new Bit2();
private Bit2 layer = new Bit2();
private Bit1 protectionBit = new Bit1();
private Bit4 bitrateIndex = new Bit4();
private Bit2 samplingRateIndex = new Bit2();
private Bit1 paddingBit = new Bit1();
private Bit1 privateBit = new Bit1();
private Bit2 channelMode = new Bit2();
private Bit2 modeExtension = new Bit2();
private Bit1 copyRight = new Bit1();
private Bit1 originalFlag = new Bit1();
private Bit2 emphasis = new Bit2();
private ByteBuffer rawBuffer;
/** tables */
private final int bitrateIndexV1L1[] = {
-1, 32000, 64000, 96000, 128000, 160000, 192000, 224000, 256000, 288000, 320000, 352000, 384000, 416000, 448000, -1
};
private final int bitrateIndexV1L2[] = {
-1, 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, 384000, -1
};
private final int bitrateIndexV1L3[] = {
-1, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, -1
};
private final int bitrateIndexV2L1[] = {
-1, 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, 176000, 192000, 224000, 256000, -1
};
private final int bitrateIndexV2L23[] = {
-1, 8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, -1
};
private final int sampleRateTable[][] = {
{11025, 12000, 8000}, // mpeg 2.5
{ -1, -1000, -1000}, // reserved
{22050, 24000, 16000}, // mpeg 2
{44100, 48000, 32000} // mpeg 1
};
/**
* {@inheritDoc}
*/
@Override
public void minimumLoad(IReadChannel channel) throws Exception {
super.setReadPosition(channel.position() - 1);
Bit3 syncBit = new Bit3();
BitLoader loader = new BitLoader(channel);
loader.load(syncBit, mpegVersion, layer, protectionBit, bitrateIndex,
samplingRateIndex, paddingBit, privateBit, channelMode,
modeExtension, copyRight, originalFlag, emphasis);
this.syncBit.set(0xFF << 3 | syncBit.get());
super.setSampleRate(sampleRateTable[mpegVersion.get()][samplingRateIndex.get()]);
setSampleNum();
super.setChannel(channelMode.get() == 3 ? 1 : 2);
setSize();
super.update();
}
/**
* set sampleNum
* @throws Exception
*/
private void setSampleNum() throws Exception {
switch(layer.get()) {
case 3: // layer1
setSampleNum(384);
break;
case 2: // layer2
setSampleNum(1152);
break;
case 1: // layer3
if(mpegVersion.get() == 3) {
// mpeg1
setSampleNum(1152);
}
else {
// mpeg2 or mpeg2.5
setSampleNum(576);
}
break;
default:
throw new Exception("value of layse is corrupt:" + layer.get());
}
}
/**
* bitrate
* @return
*/
public int getBitrate() {
if(mpegVersion.get() == 0 || mpegVersion.get() == 2) { // version 2 or 2.5
if(layer.get() == 3) { // layer1
return bitrateIndexV2L1[bitrateIndex.get()];
}
else if(layer.get() == 2 || layer.get() == 1) { // layer2,3
return bitrateIndexV2L23[bitrateIndex.get()];
}
}
if(mpegVersion.get() == 3) { // version 1
if(layer.get() == 1) { // layer3
return bitrateIndexV1L3[bitrateIndex.get()];
}
else if(layer.get() == 2) { // layer2
return bitrateIndexV1L2[bitrateIndex.get()];
}
else if(layer.get() == 3) { // layer1
return bitrateIndexV1L1[bitrateIndex.get()];
}
}
return -1;
}
/**
* calcurate size
*/
private void setSize() {
if(layer.get() == 3) { // layer1
super.setSize((int)Math.floor((12f * getBitrate() / getSampleRate() + paddingBit.get()) * 4));
}
else if(layer.get() == 2) { // layer2
super.setSize((int)Math.floor(144f * getBitrate() / getSampleRate() + paddingBit.get()));
}
else if(layer.get() == 1) { // layer3
if(mpegVersion.get() == 3) { // version1
super.setSize((int)Math.floor(144f * getBitrate() / getSampleRate() + paddingBit.get()));
}
else {
super.setSize((int)Math.floor(72f * getBitrate() / getSampleRate() + paddingBit.get()));
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void load(IReadChannel channel) throws Exception {
channel.position(getReadPosition() + 4);
rawBuffer = BufferUtil.safeRead(channel, getSize() - 4);
super.update();
}
/**
* {@inheritDoc}
*/
@Override
protected void requestUpdate() throws Exception {
if(rawBuffer == null) {
throw new Exception("rawBuffer is undefined");
}
BitConnector connector = new BitConnector();
super.setData(BufferUtil.connect(
connector.connect(syncBit, mpegVersion, layer, protectionBit,
bitrateIndex, samplingRateIndex, paddingBit, privateBit,
channelMode, modeExtension, copyRight, originalFlag, emphasis),
rawBuffer));
}
/**
* {@inheritDoc}
*/
@Override
public ByteBuffer getPackBuffer() throws Exception {
return getData();
}
}