/*
* 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.*;
import davaguine.jmac.tools.ByteBuffer;
import davaguine.jmac.tools.File;
import davaguine.jmac.tools.JMACException;
import java.io.IOException;
import java.util.Arrays;
/**
* Author: Dmitry Vaguine
* Date: 04.03.2004
* Time: 14:51:31
*/
public class APEDecompressOld extends IAPEDecompress {
public APEDecompressOld(APEInfo pAPEInfo) {
this(pAPEInfo, -1, -1);
}
public APEDecompressOld(APEInfo pAPEInfo, int nStartBlock) {
this(pAPEInfo, nStartBlock, -1);
}
public APEDecompressOld(APEInfo pAPEInfo, int nStartBlock, int nFinishBlock) {
// open / analyze the file
m_spAPEInfo = pAPEInfo;
// version check (this implementation only works with 3.92 and earlier files)
if (getApeInfoFileVersion() > 3920)
throw new JMACException("Wrong Version");
// create the buffer
m_nBlockAlign = getApeInfoBlockAlign();
// initialize other stuff
m_nBufferTail = 0;
m_bDecompressorInitialized = false;
m_nCurrentFrame = 0;
m_nCurrentBlock = 0;
// set the "real" start and finish blocks
m_nStartBlock = (nStartBlock < 0) ? 0 : Math.min(nStartBlock, getApeInfoTotalBlocks());
m_nFinishBlock = (nFinishBlock < 0) ? getApeInfoTotalBlocks() : Math.min(nFinishBlock, getApeInfoTotalBlocks());
m_bIsRanged = (m_nStartBlock != 0) || (m_nFinishBlock != getApeInfoTotalBlocks());
}
public int GetData(byte[] pBuffer, int nBlocks) throws IOException {
InitializeDecompressor();
// cap
int nBlocksUntilFinish = m_nFinishBlock - m_nCurrentBlock;
nBlocks = Math.min(nBlocks, nBlocksUntilFinish);
int nBlocksRetrieved = 0;
//fulfill as much of the request as possible
int nTotalBytesNeeded = nBlocks * m_nBlockAlign;
int nBytesLeft = nTotalBytesNeeded;
int nBlocksDecoded = 1;
while (nBytesLeft > 0 && nBlocksDecoded > 0) {
//empty the buffer
int nBytesAvailable = m_nBufferTail;
int nIntialBytes = Math.min(nBytesLeft, nBytesAvailable);
if (nIntialBytes > 0) {
System.arraycopy(m_spBuffer, 0, pBuffer, nTotalBytesNeeded - nBytesLeft, nIntialBytes);
if ((m_nBufferTail - nIntialBytes) > 0)
System.arraycopy(m_spBuffer, nIntialBytes, m_spBuffer, 0, m_nBufferTail - nIntialBytes);
nBytesLeft -= nIntialBytes;
m_nBufferTail -= nIntialBytes;
}
//decode more
if (nBytesLeft > 0) {
output.reset(m_spBuffer, m_nBufferTail);
nBlocksDecoded = m_UnMAC.DecompressFrame(output, m_nCurrentFrame++);
m_nBufferTail += (nBlocksDecoded * m_nBlockAlign);
}
}
nBlocksRetrieved = (nTotalBytesNeeded - nBytesLeft) / m_nBlockAlign;
// update the position
m_nCurrentBlock += nBlocksRetrieved;
return nBlocksRetrieved;
}
public void Seek(int nBlockOffset) throws IOException {
InitializeDecompressor();
// use the offset
nBlockOffset += m_nStartBlock;
// cap (to prevent seeking too far)
if (nBlockOffset >= m_nFinishBlock)
nBlockOffset = m_nFinishBlock - 1;
if (nBlockOffset < m_nStartBlock)
nBlockOffset = m_nStartBlock;
// flush the buffer
m_nBufferTail = 0;
// seek to the perfect location
int nBaseFrame = nBlockOffset / getApeInfoBlocksPerFrame();
int nBlocksToSkip = nBlockOffset % getApeInfoBlocksPerFrame();
int nBytesToSkip = nBlocksToSkip * m_nBlockAlign;
// skip necessary blocks
int nMaximumDecompressedFrameBytes = m_nBlockAlign * getApeInfoBlocksPerFrame();
byte[] pTempBuffer = new byte[nMaximumDecompressedFrameBytes + 16];
Arrays.fill(pTempBuffer, (byte) 0);
m_nCurrentFrame = nBaseFrame;
output.reset(pTempBuffer);
int nBlocksDecoded = m_UnMAC.DecompressFrame(output, m_nCurrentFrame++);
if (nBlocksDecoded == -1)
throw new JMACException("Error While Decoding");
int nBytesToKeep = (nBlocksDecoded * m_nBlockAlign) - nBytesToSkip;
System.arraycopy(pTempBuffer, nBytesToSkip, m_spBuffer, m_nBufferTail, nBytesToKeep);
m_nBufferTail += nBytesToKeep;
m_nCurrentBlock = nBlockOffset;
}
// buffer
protected byte[] m_spBuffer;
protected int m_nBufferTail;
protected ByteBuffer output = new ByteBuffer();
// file info
protected int m_nBlockAlign;
protected int m_nCurrentFrame;
// start / finish information
protected int m_nStartBlock;
protected int m_nFinishBlock;
protected int m_nCurrentBlock;
protected boolean m_bIsRanged;
// decoding tools
protected UnMAC m_UnMAC = new UnMAC();
protected APEInfo m_spAPEInfo;
protected boolean m_bDecompressorInitialized;
protected void InitializeDecompressor() throws IOException {
// check if we have anything to do
if (m_bDecompressorInitialized)
return;
// initialize the core
m_UnMAC.Initialize(this);
int nMaximumDecompressedFrameBytes = m_nBlockAlign * getApeInfoBlocksPerFrame();
int nTotalBufferBytes = Math.max(65536, (nMaximumDecompressedFrameBytes + 16) * 2);
m_spBuffer = new byte[nTotalBufferBytes];
// update the initialized flag
m_bDecompressorInitialized = true;
// seek to the beginning
Seek(0);
}
public int getApeInfoDecompressCurrentBlock() {
return m_nCurrentBlock - m_nStartBlock;
}
public int getApeInfoDecompressCurrentMS() {
int nSampleRate = m_spAPEInfo.getApeInfoSampleRate();
if (nSampleRate > 0)
return (int) ((m_nCurrentBlock * 1000L) / nSampleRate);
return 0;
}
public int getApeInfoDecompressTotalBlocks() {
return m_nFinishBlock - m_nStartBlock;
}
public int getApeInfoDecompressLengthMS() {
int nSampleRate = m_spAPEInfo.getApeInfoSampleRate();
if (nSampleRate > 0)
return (int) (((m_nFinishBlock - m_nStartBlock) * 1000L) / nSampleRate);
return 0;
}
public int getApeInfoDecompressCurrentBitRate() throws IOException {
return m_spAPEInfo.getApeInfoFrameBitrate(m_nCurrentFrame);
}
public int getApeInfoDecompressAverageBitrate() throws IOException {
if (m_bIsRanged) {
// figure the frame range
int nBlocksPerFrame = m_spAPEInfo.getApeInfoBlocksPerFrame();
int nStartFrame = m_nStartBlock / nBlocksPerFrame;
int nFinishFrame = (m_nFinishBlock + nBlocksPerFrame - 1) / nBlocksPerFrame;
// get the number of bytes in the first and last frame
int nTotalBytes = (m_spAPEInfo.getApeInfoFrameBytes(nStartFrame) * (m_nStartBlock % nBlocksPerFrame)) / nBlocksPerFrame;
if (nFinishFrame != nStartFrame)
nTotalBytes += (m_spAPEInfo.getApeInfoFrameBytes(nFinishFrame) * (m_nFinishBlock % nBlocksPerFrame)) / nBlocksPerFrame;
// get the number of bytes in between
int nTotalFrames = m_spAPEInfo.getApeInfoTotalFrames();
for (int nFrame = nStartFrame + 1; (nFrame < nFinishFrame) && (nFrame < nTotalFrames); nFrame++)
nTotalBytes += m_spAPEInfo.getApeInfoFrameBytes(nFrame);
// figure the bitrate
int nTotalMS = (int) (((m_nFinishBlock - m_nStartBlock) * 1000L) / m_spAPEInfo.getApeInfoSampleRate());
if (nTotalMS != 0)
return (nTotalBytes * 8) / nTotalMS;
} else {
return m_spAPEInfo.getApeInfoAverageBitrate();
}
return 0;
}
public int getApeInfoWavHeaderBytes() {
if (m_bIsRanged)
return WaveHeader.WAVE_HEADER_BYTES;
return m_spAPEInfo.getApeInfoWavHeaderBytes();
}
public byte[] getApeInfoWavHeaderData(int nMaxBytes) {
if (m_bIsRanged) {
if (WaveHeader.WAVE_HEADER_BYTES > nMaxBytes)
return null;
else {
WaveFormat wfeFormat = m_spAPEInfo.getApeInfoWaveFormatEx();
WaveHeader WAVHeader = new WaveHeader();
WaveHeader.FillWaveHeader(WAVHeader, (m_nFinishBlock - m_nStartBlock) * m_spAPEInfo.getApeInfoBlockAlign(), wfeFormat, 0);
return WAVHeader.write();
}
}
return m_spAPEInfo.getApeInfoWavHeaderData(nMaxBytes);
}
public int getApeInfoWavTerminatingBytes() {
if (m_bIsRanged)
return 0;
else
return m_spAPEInfo.getApeInfoWavTerminatingBytes();
}
public byte[] getApeInfoWavTerminatingData(int nMaxBytes) throws IOException {
if (m_bIsRanged)
return null;
else
return m_spAPEInfo.getApeInfoWavTerminatingData(nMaxBytes);
}
public WaveFormat getApeInfoWaveFormatEx() {
return m_spAPEInfo.getApeInfoWaveFormatEx();
}
public File getApeInfoIoSource() {
return m_spAPEInfo.getApeInfoIoSource();
}
public int getApeInfoBlocksPerFrame() {
return m_spAPEInfo.getApeInfoBlocksPerFrame();
}
public int getApeInfoFileVersion() {
return m_spAPEInfo.getApeInfoFileVersion();
}
public int getApeInfoCompressionLevel() {
return m_spAPEInfo.getApeInfoCompressionLevel();
}
public int getApeInfoFormatFlags() {
return m_spAPEInfo.getApeInfoFormatFlags();
}
public int getApeInfoSampleRate() {
return m_spAPEInfo.getApeInfoSampleRate();
}
public int getApeInfoBitsPerSample() {
return m_spAPEInfo.getApeInfoBitsPerSample();
}
public int getApeInfoBytesPerSample() {
return m_spAPEInfo.getApeInfoBytesPerSample();
}
public int getApeInfoChannels() {
return m_spAPEInfo.getApeInfoChannels();
}
public int getApeInfoBlockAlign() {
return m_spAPEInfo.getApeInfoBlockAlign();
}
public int getApeInfoFinalFrameBlocks() {
return m_spAPEInfo.getApeInfoFinalFrameBlocks();
}
public int getApeInfoTotalFrames() {
return m_spAPEInfo.getApeInfoTotalFrames();
}
public int getApeInfoWavDataBytes() {
return m_spAPEInfo.getApeInfoWavDataBytes();
}
public int getApeInfoWavTotalBytes() {
return m_spAPEInfo.getApeInfoWavTotalBytes();
}
public int getApeInfoApeTotalBytes() {
return m_spAPEInfo.getApeInfoApeTotalBytes();
}
public int getApeInfoTotalBlocks() {
return m_spAPEInfo.getApeInfoTotalBlocks();
}
public int getApeInfoLengthMs() {
return m_spAPEInfo.getApeInfoLengthMs();
}
public int getApeInfoAverageBitrate() {
return m_spAPEInfo.getApeInfoAverageBitrate();
}
public int getApeInfoSeekByte(int nFrame) {
return m_spAPEInfo.getApeInfoSeekByte(nFrame);
}
public int getApeInfoFrameBytes(int nFrame) throws IOException {
return m_spAPEInfo.getApeInfoFrameBytes(nFrame);
}
public int getApeInfoFrameBlocks(int nFrame) {
return m_spAPEInfo.getApeInfoFrameBlocks(nFrame);
}
public int getApeInfoFrameBitrate(int nFrame) throws IOException {
return m_spAPEInfo.getApeInfoFrameBitrate(nFrame);
}
public int getApeInfoDecompressedBitrate() {
return m_spAPEInfo.getApeInfoDecompressedBitrate();
}
public int getApeInfoPeakLevel() {
return m_spAPEInfo.getApeInfoPeakLevel();
}
public int getApeInfoSeekBit(int nFrame) {
return m_spAPEInfo.getApeInfoSeekBit(nFrame);
}
public APETag getApeInfoTag() {
return m_spAPEInfo.getApeInfoTag();
}
public APEFileInfo getApeInfoInternalInfo() {
return m_spAPEInfo.getApeInfoInternalInfo();
}
}