/*
* 21.04.2004 Original verion. davagin@udm.ru.
*-----------------------------------------------------------------------
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*----------------------------------------------------------------------
*/
package davaguine.jmac.decoder;
import davaguine.jmac.info.APEHeader;
import davaguine.jmac.info.SpecialFrame;
import davaguine.jmac.info.WaveFormat;
import davaguine.jmac.tools.ByteBuffer;
import davaguine.jmac.tools.Crc32;
import davaguine.jmac.tools.JMACException;
import davaguine.jmac.tools.Prepare;
import java.io.IOException;
/**
* Author: Dmitry Vaguine
* Date: 04.03.2004
* Time: 14:51:31
*/
public class UnMAC {
//construction/destruction
public UnMAC() {
//initialize member variables
m_bInitialized = false;
m_nRealFrame = 0;
m_LastDecodedFrameIndex = -1;
m_pAPEDecompress = null;
m_pAPEDecompressCore = null;
m_pPrepare = null;
m_nBlocksProcessed = 0;
}
//functions
public void Initialize(IAPEDecompress pAPEDecompress) {
//uninitialize if it is currently initialized
if (m_bInitialized)
Uninitialize();
if (pAPEDecompress == null) {
Uninitialize();
throw new JMACException("Error Initializing UnMAC");
}
//set the member pointer to the IAPEDecompress class
m_pAPEDecompress = pAPEDecompress;
//set the last decode frame to -1 so it forces a seek on start
m_LastDecodedFrameIndex = -1;
m_pAPEDecompressCore = new APEDecompressCore(pAPEDecompress);
m_pPrepare = new Prepare();
//set the initialized flag to TRUE
m_bInitialized = true;
m_wfeInput = m_pAPEDecompress.getApeInfoWaveFormatEx();
}
public void Uninitialize() {
if (m_bInitialized) {
m_pAPEDecompressCore = null;
m_pPrepare = null;
//clear the APE info pointer
m_pAPEDecompress = null;
//set the last decoded frame again
m_LastDecodedFrameIndex = -1;
//set the initialized flag to FALSE
m_bInitialized = false;
}
}
public int DecompressFrame(ByteBuffer pOutputData, int FrameIndex) throws IOException {
return DecompressFrameOld(pOutputData, FrameIndex);
}
public void SeekToFrame(int FrameIndex) throws IOException {
if (m_pAPEDecompress.getApeInfoFileVersion() > 3800) {
if ((m_LastDecodedFrameIndex == -1) || ((FrameIndex - 1) != m_LastDecodedFrameIndex)) {
int SeekRemainder = (m_pAPEDecompress.getApeInfoSeekByte(FrameIndex) - m_pAPEDecompress.getApeInfoSeekByte(0)) % 4;
m_pAPEDecompressCore.GetUnBitArrray().FillAndResetBitArray(m_nRealFrame == FrameIndex ? -1 : m_pAPEDecompress.getApeInfoSeekByte(FrameIndex) - SeekRemainder, SeekRemainder * 8);
m_nRealFrame = FrameIndex;
} else
m_pAPEDecompressCore.GetUnBitArrray().AdvanceToByteBoundary();
} else {
if ((m_LastDecodedFrameIndex == -1) || ((FrameIndex - 1) != m_LastDecodedFrameIndex)) {
m_pAPEDecompressCore.GetUnBitArrray().FillAndResetBitArray(m_nRealFrame == FrameIndex ? -1 : m_pAPEDecompress.getApeInfoSeekByte(FrameIndex), m_pAPEDecompress.getApeInfoSeekBit(FrameIndex));
m_nRealFrame = FrameIndex;
}
}
}
//data members
private boolean m_bInitialized;
private int m_LastDecodedFrameIndex;
private int m_nRealFrame;
private IAPEDecompress m_pAPEDecompress;
private Prepare m_pPrepare;
private APEDecompressCore m_pAPEDecompressCore;
//functions
private int DecompressFrameOld(ByteBuffer pOutputData, int FrameIndex) throws IOException {
//error check the parameters (too high of a frame index, etc.)
if (FrameIndex >= m_pAPEDecompress.getApeInfoTotalFrames())
return 0;
//get the number of samples in the frame
int nBlocks = 0;
nBlocks = ((FrameIndex + 1) >= m_pAPEDecompress.getApeInfoTotalFrames()) ? m_pAPEDecompress.getApeInfoFinalFrameBlocks() : m_pAPEDecompress.getApeInfoBlocksPerFrame();
if (nBlocks == 0)
throw new JMACException("Invalid Frame Index"); //nothing to do (file must be zero length) (have to return error)
//take care of seeking and frame alignment
SeekToFrame(FrameIndex);
//get the checksum
long nSpecialCodes = 0;
long nStoredCRC = 0;
if ((m_pAPEDecompress.getApeInfoFormatFlags() & APEHeader.MAC_FORMAT_FLAG_CRC) <= 0) {
nStoredCRC = m_pAPEDecompressCore.GetUnBitArrray().DecodeValue(DecodeValueMethod.DECODE_VALUE_METHOD_UNSIGNED_RICE, 30);
if (nStoredCRC == 0)
nSpecialCodes = SpecialFrame.SPECIAL_FRAME_LEFT_SILENCE | SpecialFrame.SPECIAL_FRAME_RIGHT_SILENCE;
} else {
nStoredCRC = m_pAPEDecompressCore.GetUnBitArrray().DecodeValue(DecodeValueMethod.DECODE_VALUE_METHOD_UNSIGNED_INT);
//get any 'special' codes if the file uses them (for silence, FALSE stereo, etc.)
nSpecialCodes = 0;
if (m_pAPEDecompress.getApeInfoFileVersion() > 3820) {
if ((nStoredCRC & 0x80000000) > 0)
nSpecialCodes = m_pAPEDecompressCore.GetUnBitArrray().DecodeValue(DecodeValueMethod.DECODE_VALUE_METHOD_UNSIGNED_INT);
nStoredCRC &= 0x7fffffff;
}
}
//decompress and convert from (x,y) -> (l,r)
//sort of int and ugly.... sorry
if (m_pAPEDecompress.getApeInfoChannels() == 2) {
m_pAPEDecompressCore.GenerateDecodedArrays(nBlocks, (int) nSpecialCodes, FrameIndex);
m_pPrepare.unprepareOld(m_pAPEDecompressCore.m_pDataX, m_pAPEDecompressCore.m_pDataY, nBlocks, m_wfeInput,
pOutputData, CRC, m_pAPEDecompress.getApeInfoFileVersion());
} else if (m_pAPEDecompress.getApeInfoChannels() == 1) {
m_pAPEDecompressCore.GenerateDecodedArrays(nBlocks, (int) nSpecialCodes, FrameIndex);
m_pPrepare.unprepareOld(m_pAPEDecompressCore.m_pDataX, null, nBlocks, m_wfeInput,
pOutputData, CRC, m_pAPEDecompress.getApeInfoFileVersion());
}
if (m_pAPEDecompress.getApeInfoFileVersion() > 3820)
CRC.finalizeCrc();
// check the CRC
if ((m_pAPEDecompress.getApeInfoFormatFlags() & APEHeader.MAC_FORMAT_FLAG_CRC) <= 0) {
long nChecksum = CalculateOldChecksum(m_pAPEDecompressCore.m_pDataX, m_pAPEDecompressCore.m_pDataY, m_pAPEDecompress.getApeInfoChannels(), nBlocks);
if (nChecksum != nStoredCRC)
throw new JMACException("Invalid Checksum");
} else {
if (CRC.getCrc() != nStoredCRC)
throw new JMACException("Invalid Checksum");
}
m_LastDecodedFrameIndex = FrameIndex;
return nBlocks;
}
private long CalculateOldChecksum(int[] pDataX, int[] pDataY, int nChannels, int nBlocks) {
long nChecksum = 0;
if (nChannels == 2) {
for (int z = 0; z < nBlocks; z++) {
int R = pDataX[z] - (pDataY[z] / 2);
int L = R + pDataY[z];
nChecksum += (Math.abs(R) + Math.abs(L));
}
} else if (nChannels == 1) {
for (int z = 0; z < nBlocks; z++)
nChecksum += Math.abs(pDataX[z]);
}
return nChecksum;
}
public int m_nBlocksProcessed;
public Crc32 CRC = new Crc32();
public long m_nStoredCRC;
public WaveFormat m_wfeInput;
}