/*
* 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.info;
import davaguine.jmac.tools.File;
import davaguine.jmac.tools.InputStreamFile;
import davaguine.jmac.tools.JMACException;
import davaguine.jmac.tools.RandomAccessFile;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
/**
* Author: Dmitry Vaguine
* Date: 04.03.2004
* Time: 14:51:31
*/
public class APEInfo {
// construction and destruction
public APEInfo(URL url) throws IOException {
this(url, null);
}
public APEInfo(URL url, APETag pTag) throws IOException {
this(url.openStream(), pTag);
}
public APEInfo(java.io.File file) throws IOException {
this(file, null);
}
public APEInfo(java.io.File file, APETag pTag) throws IOException {
this(new RandomAccessFile(file, "r"), pTag);
}
public APEInfo(InputStream pIO) throws IOException {
this(pIO, null);
}
public APEInfo(InputStream pIO, APETag pTag) throws IOException {
this(new InputStreamFile(pIO), pTag);
}
public APEInfo(File pIO) throws IOException {
this(pIO, null);
}
public APEInfo(File pIO, APETag pTag) throws IOException {
// open the file
m_spIO = pIO;
// get the file information
GetFileInformation();
// get the tag (do this second so that we don't do it on failure)
if (pTag == null) {
// we don't want to analyze right away for non-local files
// since a single I/O object is shared, we can't tag and read at the same time (i.e. in multiple threads)
m_spAPETag = new APETag(m_spIO, pIO.isLocal());
} else
m_spAPETag = pTag;
}
public void close() throws IOException {
m_APEFileInfo.spWaveHeaderData = null;
m_APEFileInfo.spSeekBitTable = null;
m_APEFileInfo.spSeekByteTable = null;
m_APEFileInfo.spAPEDescriptor = null;
m_spAPETag = null;
// re-initialize variables
m_APEFileInfo.nSeekTableElements = 0;
m_bHasFileInformationLoaded = false;
}
public int getApeInfoFileVersion() {
return m_APEFileInfo.nVersion;
}
public int getApeInfoCompressionLevel() {
return m_APEFileInfo.nCompressionLevel;
}
public int getApeInfoFormatFlags() {
return m_APEFileInfo.nFormatFlags;
}
public int getApeInfoSampleRate() {
return m_APEFileInfo.nSampleRate;
}
public int getApeInfoBitsPerSample() {
return m_APEFileInfo.nBitsPerSample;
}
public int getApeInfoBytesPerSample() {
return m_APEFileInfo.nBytesPerSample;
}
public int getApeInfoChannels() {
return m_APEFileInfo.nChannels;
}
public int getApeInfoBlockAlign() {
return m_APEFileInfo.nBlockAlign;
}
public int getApeInfoBlocksPerFrame() {
return m_APEFileInfo.nBlocksPerFrame;
}
public int getApeInfoFinalFrameBlocks() {
return m_APEFileInfo.nFinalFrameBlocks;
}
public int getApeInfoTotalFrames() {
return m_APEFileInfo.nTotalFrames;
}
public int getApeInfoWavHeaderBytes() {
return m_APEFileInfo.nWAVHeaderBytes;
}
public int getApeInfoWavTerminatingBytes() {
return m_APEFileInfo.nWAVTerminatingBytes;
}
public int getApeInfoWavDataBytes() {
return m_APEFileInfo.nWAVDataBytes;
}
public int getApeInfoWavTotalBytes() {
return m_APEFileInfo.nWAVTotalBytes;
}
public int getApeInfoApeTotalBytes() {
return m_APEFileInfo.nAPETotalBytes;
}
public int getApeInfoTotalBlocks() {
return m_APEFileInfo.nTotalBlocks;
}
public int getApeInfoLengthMs() {
return m_APEFileInfo.nLengthMS;
}
public int getApeInfoAverageBitrate() {
return m_APEFileInfo.nAverageBitrate;
}
public int getApeInfoSeekByte(int nFrame) {
return (nFrame < 0 || nFrame >= m_APEFileInfo.nTotalFrames) ? 0 : m_APEFileInfo.spSeekByteTable[nFrame] + m_APEFileInfo.nJunkHeaderBytes;
}
public int getApeInfoFrameBytes(int nFrame) throws IOException {
if ((nFrame < 0) || (nFrame >= m_APEFileInfo.nTotalFrames))
return -1;
else {
if (nFrame != (m_APEFileInfo.nTotalFrames - 1))
return getApeInfoSeekByte(nFrame + 1) - getApeInfoSeekByte(nFrame);
else {
if (m_spIO.isLocal())
return (int) m_spIO.length() - m_spAPETag.GetTagBytes() - m_APEFileInfo.nWAVTerminatingBytes - getApeInfoSeekByte(nFrame);
else if (nFrame > 0)
return getApeInfoSeekByte(nFrame) - getApeInfoSeekByte(nFrame - 1);
else
return -1;
}
}
}
public int getApeInfoFrameBlocks(int nFrame) {
if ((nFrame < 0) || (nFrame >= m_APEFileInfo.nTotalFrames))
return -1;
else {
if (nFrame != (m_APEFileInfo.nTotalFrames - 1))
return m_APEFileInfo.nBlocksPerFrame;
else
return m_APEFileInfo.nFinalFrameBlocks;
}
}
public int getApeInfoFrameBitrate(int nFrame) throws IOException {
int nFrameBytes = getApeInfoFrameBytes(nFrame);
int nFrameBlocks = getApeInfoFrameBlocks(nFrame);
if ((nFrameBytes > 0) && (nFrameBlocks > 0) && m_APEFileInfo.nSampleRate > 0) {
int nFrameMS = (nFrameBlocks * 1000) / m_APEFileInfo.nSampleRate;
if (nFrameMS != 0) {
return (nFrameBytes * 8) / nFrameMS;
}
}
return m_APEFileInfo.nAverageBitrate;
}
public int getApeInfoDecompressedBitrate() {
return m_APEFileInfo.nDecompressedBitrate;
}
public int getApeInfoPeakLevel() {
return m_APEFileInfo.nPeakLevel;
}
public int getApeInfoSeekBit(int nFrame) {
if (getApeInfoFileVersion() > 3800)
return 0;
else {
if (nFrame < 0 || nFrame >= m_APEFileInfo.nTotalFrames)
return 0;
else
return m_APEFileInfo.spSeekBitTable[nFrame];
}
}
public WaveFormat getApeInfoWaveFormatEx() {
final WaveFormat pWaveFormatEx = new WaveFormat();
WaveFormat.FillWaveFormatEx(pWaveFormatEx, m_APEFileInfo.nSampleRate, m_APEFileInfo.nBitsPerSample, m_APEFileInfo.nChannels);
return pWaveFormatEx;
}
public byte[] getApeInfoWavHeaderData(int nMaxBytes) {
if ((m_APEFileInfo.nFormatFlags & APEHeader.MAC_FORMAT_FLAG_CREATE_WAV_HEADER) > 0) {
if (WaveHeader.WAVE_HEADER_BYTES > nMaxBytes)
return null;
else {
WaveFormat wfeFormat = getApeInfoWaveFormatEx();
WaveHeader WAVHeader = new WaveHeader();
WaveHeader.FillWaveHeader(WAVHeader, m_APEFileInfo.nWAVDataBytes, wfeFormat, m_APEFileInfo.nWAVTerminatingBytes);
return WAVHeader.write();
}
} else {
if (m_APEFileInfo.nWAVHeaderBytes > nMaxBytes)
return null;
else {
byte[] pBuffer = new byte[m_APEFileInfo.nWAVHeaderBytes];
System.arraycopy(m_APEFileInfo.spWaveHeaderData, 0, pBuffer, 0, m_APEFileInfo.nWAVHeaderBytes);
return pBuffer;
}
}
}
public File getApeInfoIoSource() {
return m_spIO;
}
public APETag getApeInfoTag() {
return m_spAPETag;
}
public byte[] getApeInfoWavTerminatingData(int nMaxBytes) throws IOException {
if (m_APEFileInfo.nWAVTerminatingBytes > nMaxBytes)
return null;
else {
if (m_APEFileInfo.nWAVTerminatingBytes > 0) {
// variables
long nOriginalFileLocation = m_spIO.getFilePointer();
// check for a tag
m_spIO.seek(m_spIO.length() - (m_spAPETag.GetTagBytes() + m_APEFileInfo.nWAVTerminatingBytes));
byte[] pBuffer = new byte[m_APEFileInfo.nWAVTerminatingBytes];
try {
m_spIO.readFully(pBuffer);
} catch (EOFException e) {
throw new JMACException("Can't Read WAV Terminating Bytes");
}
// restore the file pointer
m_spIO.seek(nOriginalFileLocation);
return pBuffer;
}
return null;
}
}
public APEFileInfo getApeInfoInternalInfo() {
return m_APEFileInfo;
}
private void GetFileInformation() throws IOException {
// quit if the file information has already been loaded
if (m_bHasFileInformationLoaded)
return;
// use a CAPEHeader class to help us analyze the file
APEHeader APEHeader = new APEHeader(m_spIO);
APEHeader.Analyze(m_APEFileInfo);
m_bHasFileInformationLoaded = true;
}
// internal variables
private boolean m_bHasFileInformationLoaded;
private File m_spIO;
private APETag m_spAPETag;
private APEFileInfo m_APEFileInfo = new APEFileInfo();
}