package com.limegroup.gnutella.metadata;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import com.limegroup.gnutella.ByteOrder;
/**
* Provide MP3 file info derived from the file header data
*
* @see #getLayer_* -> mp3 layer (3 or "Layer III", etc)
* @see #getMode -> mode type strings (stereo, dual channel, etc)
* @see #getFrequency -> available frequencies (32000, 44100, etc) khz
* @see #getVersion_* -> mp3 file version (2.0, or "MPEG Version 2.0")
* @see #getHeaderBitRate -> constant bit rates(CBR) (128, 256, etc) kps
* @see com.limegroup.gnutella.ByteOrder
*
* @author cHANCE mOORE, ctmoore [at] gottapee [dot] com - 30 July 2002
* One of the Sindhis (both?), limewire team
* Gustav "Grim Reaper" Munkby, grd@swipnet.se
*
* TODO: add tests?
*/
//34567890123456789012345678901234567890123456789012345678901234567890123456789
public final class MP3Info {
/**
* the canonical localized mp3 file name
*/
private final String _file;
/**
* 1st mp3 file's header; 4 bytes(combined) at beginning after any ID tags
* all the standard getters reference the header data
*/
private int _header;
/**
* represenation of the Variable bit rate header, if one exists
* @see MP3Info$VBRHeader
*/
private VBRHeader _vbrHeader;
/**
* Data holder for Xing variable bit rate headers
*/
final class VBRHeader {
/**
* initially -1 as most fields are optional
* fields without getters are accessed through the MP3Info
*/
private int numFrames = -1;
private int numBytes = -1;
private int scale = -1;
private byte[] toc;
/**
* @return int suggested encoding quality, scaled 1 to 100
*/
int getScale() {
return scale;
}
/**
* Table of Contents holds byte pos -> % of song complete, 1 to 100%
* @return byte[] These bytes are raw java bytes, need to be "& 255"
*/
byte[] getTableOfContents() {
return toc;
}
/**
* VBR header only returns a rate when frames and bytes are supplied
*/
int getBitRate() {
if (numFrames != -1 && numBytes != -1) {
double tpf = 0;
switch (getLayerIndex()) {
case 1:
case 2:
tpf = 1152D;
break;
case 3:
tpf = 384D;
} //new double[]{ -1, 1152, 1152, 384 }
tpf /= getFrequency();
if( (getVersion_Numeric() == 2) || //MPEG_V_2
(getVersion_Numeric() == 0) ) { //MPEG_V_25
tpf /= 2;
}
return (int)( (numBytes * 8) / (tpf * numFrames * 1000) );
}
return -1;
}
/**
*
*/
int getLengthInSeconds() {
if (numFrames != -1) {
double tpf = 0;
switch (getLayerIndex()) {
case 1:
case 2:
tpf = 1152D;
break;
case 3:
tpf = 384D;
} //new double[]{ -1, 1152, 1152, 384 }
tpf /= getFrequency();
if( (getVersion_Numeric() == 2) || //MPEG_V_2
(getVersion_Numeric() == 0) ) { //MPEG_V_25
tpf /= 2;
}
return (int)( tpf * numFrames );
}
return -1;
}
}
/**
* An MPEG audio file is built up from smaller parts called frames, which
* are generally independent items. Each frame has its own header and audio
* data that follows. There is NO MPEG file header; therefore, you can cut
* any part of MPEG file and play it correctly (cut on frame boundaries!),
* excluding MPEG 1 Layer III frames which are often dependent on another.
*
* To read info about an MPEG file, you can find the first frame, read its
* header and assume that the other frames are the same. Exceptions to this
* are VBR (variable bit rate) and ABR (average bit rate) files. The frame
* header is constituted by the very first four bytes (32bits) in a frame.
* The first 11 bits are always set on(1) and they're called "frame sync".
* Frame CRC is optional and 16 bits long; it follows the frame header.
* After the CRC comes the audio data.
*
* ::EXAMPLE:: MP3 file header format (4 byte length or 32 bits)
* byte[4] = { -1, -5, 80, 108 }
* -1 << 24 + -5 << 16 + 80 << 08 + 108 << 0 {HdrCRC}
* 11111111 11101010 00110000 11000000 {0000}
* AAAAAAAA AAABBCCD EEEEFFGH IIJJKLMM {ZZZZ}
*
* Label, Position(bits), Description
* A (31-21) Frame sync
* All bits set (1)
* B (20,19) MPEG Audio version ID
* 00 - MPEG Ver 2.5, 01 - reserved, 10 - Ver 2, 11 - Ver 1
* Note: MPEG Ver 2.5 is not official; bit # 20 indicates 2.5
* C (18,17) Layer description
* 00 - reserved, 01 - Layer III, 10 - Layer II, 11 - Layer I
* D (16) Protection bit
* 0 - None, 1 - Protected by CRC (16bit crc follows header)
* E (15,12) Bitrate index, version and layer
* bits V1,L1 V1,L2 V1,L3 V2,L1 V2, L2 & L3
* F (11,10)
* G (9) Padding bit
* 0 - frame not padded, 1 - frame padded with one extra slot
* Note: Padding is used to fit the bit rates exactly.
* H (8) Private bit
* 0 - not private, 1 - private
* Note: May be freely used for other needs of an application.
* I (7,6) Channel Mode
* 00 - Stereo, 01 - Joint stereo, 10 - Dual (Stereo), 11 - Mono
* J (5,4) Mode extension (Only if Joint stereo)
* Used to join data; bits dynamically generated by an encoder.
* K (3) Copyright
* 0 - Audio is not copyrighted, 1 - Audio is marked copyrighted
* L (2) Original
* 0 - Copy of original media, 1 - Original media
* M (1,0) Emphasis
* 00 - none, 01 - 50/15 ms, 10 - reserved, 11 - CCIT J.17
* Z (32-35) CRC !!OPTIONAL!!
* Note: NOT part of header, just appended on end when needed
*
* We read in bytes from the beginning of the mp3 file looking for
* the 4 byte header; we can't assume it starts at byte 0 because
* ID3 tags may be prepended before the first valid header.
* The loop below strolls through buffered chunks of the file
* looking for the header. As an optimization, we check the first
* 10 bytes initially as it may contain the header; if it doesn't
* we then check the first 10 bytes for an ID3v2 header and fetch
* the tag's length, skipping those bytes leading us directly
* to the header. If neither are found, it's a brute force search.
* With each chunk, we step forward one byte at a time, and test
* the current byte plus the next 3 bytes for a valid mp3 header.
*
* @exception java.io.IOException mp3 fileName had no valid header
*/
public MP3Info(String file) throws IOException {
_file = file;
//TODO:use 1.4 BufferMaps
int i = 0; //reusable loop variant
int pos = 0; //position in file, start at the beginning, duh...
int adjustedEOB = 0;//adjusted end depending on actual bytes read
int c = 0; //number of actual bytes read from file
FileInputStream fis = null;
byte[] buf = new byte[2048];
try {
fis = new FileInputStream(_file);
//initially check the first few bytes
c = fis.read(buf, 0, buf.length);
if( c < 4 )
throw new IOException("early EOF, tiny file?");
//check for ID3 tag
//officially ID3, some tags incorrectly contain lowercase
if ( (buf[0] == 'i' || buf[0] == 'I')
&& (buf[1] == 'd' || buf[1] == 'D')
&& (buf[2] == '3')
) {
//length of tag format is specified in the ID3v2 standard
//28 bits amongst four bytes, first bit of each byte is 0
i = buf[6] << 7 | buf[7] << 7 | buf[8] << 7 | buf[9];
if (i > 0) { //skip indicated tag length and read header
i += 10;
}
else if (i < 0) { //clear bad data
i = 0;
}
}
endheadersearch:
do {
if (pos < buf.length - 3) { //is first time?
adjustedEOB = c - 3;
}
else {
i = 0; //reset i except first time
adjustedEOB = c; //already offset
}
for ( ; i < adjustedEOB; i++ ) {
///////
//quicktest, first byte must be 256
//quickly skip more expensive tests below, if possible
if (buf[i] != -1 || (buf[i+1] & 255) < 224) {
continue;
}
//build a header to test
_header
= ( ByteOrder.ubyte2int(buf[i+3]) )
| ( ByteOrder.ubyte2int(buf[i+2]) << 8 )
| ( ByteOrder.ubyte2int(buf[i+1]) << 16 )
| ( ByteOrder.ubyte2int(buf[i ]) << 24 )
;
// detect if valid header or just four different chars
if ( //(getFrameSync() ==2047) && //tested above
(getVersionIndex() != 1) &&
(getLayerIndex() != 0) &&
(getBitrateIndex() != 0) &&
(getBitrateIndex() != 15) &&
(getFrequencyIndex() != 3) &&
(getEmphasisIndex() != 2) ) {
pos += i;
break endheadersearch;
}
}
//save last 3 bytes to test with next chunk
// check trailing end of last chunk with start of new chunk
// ie. last 3 bytes with first new byte
// last 2 bytes with first 2 new bytes
// last 1 byte with first 3 new bytes
if (adjustedEOB != -1) { //skip when EOF
buf[0] = buf[c-3];
buf[1] = buf[c-2];
buf[2] = buf[c-1];
}
pos += c - 3;
c = fis.read(buf, 3, buf.length-3); //read next chunk
if( c < 6 ) //not enough to make a difference
throw new IOException("MP3 Header not found.");
} while (c != -1 && pos < 100000); //c is # of bytes read; until EOF
//stop checking after first 100k, could be corrupted/infected file
if (c == -1 || pos >= 100000) { // what the $#*!
_header = 0;
throw new IOException("MP3 header not found.");
}
// Looking for the VBR
// @see loadVBRHeeader for VBR format specifics
// advance to check where Xing header would be
// make sure we have enough data to test/work with
// 120 is total 'possible' VBR length
// 36 max bytes to skip
// 3 is to cover length of VBR header
int need = buf.length // total we have.
- i // where we're currently at
- 3 // VBR header
- 120 // max possible VBR length
- 36; // max bytes to skip
if (need < 0) { //special case, we need more data
need = -need; // flip need to be positive.
i -= need; // shift our offset down by the amount we'll be moving
int j = 0;
for (; need < buf.length; j++, need++ ) { // shift data
buf[j] = buf[need];
}
// IMPORTANT:
// j is NOT equal to i for the following reason:
// i is where we last stopped reading data from the buffer.
// j is where the last bit of valid information in the buffer is.
// we must continue reading from the buffer using i, but we must
// fill up the the rest of the buffer from j on.
//read more, starting at where we last have valid data.
c = fis.read(buf, j, buf.length-j);
}
if ( getVersionIndex() == 3 ) { // mpeg version 1
i += (getModeIndex()==3 ? 21 : 36);
}
else { // mpeg version 2 or 2.5
i += (getModeIndex()==3 ? 23 : 21);
}
// Doh!! not all VBR files will have correct tags, it's optional
switch (buf[i+0]) {
case 88: //'X':
if (((buf[i+1] == 'i' || buf[i+1] == 'I')
&& (buf[i+2] == 'n' || buf[i+2] == 'N')
&& (buf[i+3] == 'g' || buf[i+3] == 'G')))
// The Xing VBR headers always begin with the four chars "Xing"
loadXingHeader(buf, i+4);
break;
case 86: //'V':
if ((buf[i+1] == 'B'
&& buf[i+2] == 'R'
&& buf[i+3] == 'I' ))
//"VBRI" is a rarely used method of tagging Fhg encoded VBRs
loadFhgHeader(buf, i+4);
break;
//case 73: //'I':
// if( buf[i+1] == 'n'
// && buf[i+2] == 'f'
// && buf[i+3] == 'o' )
// LAME uses "Info" to tag LAME CBR/ABR files
// there is no VBR data, but may provide useful LAME & ABR data
// 4 skips VBR header, 109 skips dead Xing tag to reach 'LAME' tag
// loadLAMETag(buf, i+4+109);
//break;
//default:
//true VBR file may not have a proper tag, to find out for sure
//read every header to calculate true variable rate, length, etc
}
} finally { //cleanup
try {
if( fis != null )
fis.close();
} catch (IOException e) {}//ignore
}
}
public int getBitRate() {
if (hasVariableBitRate()) {
int i = _vbrHeader.getBitRate();
if (i != -1) {
return i;
}
}
long size = getFileSize();
double mediumFrameSize =
( (getLayerIndex() == 3 ? 12000 : 144000) * getHeaderBitRate() )
/
( (double)getFrequency() );
/* FrameSizes formula
mpeg v1: FrameSize = 12 * BitRate / SampleRate + Padding
mpeg v2: FrameSize = 144 * BitRate / SampleRate + Padding
bitrate is kbps & sample rate in Hz, so multiply BitRate by 1000
*/
// get average frame size by dividing size by the # of frames
int retInt = (int)( (size / (size/mediumFrameSize) * getFrequency())
/
(getLayerIndex() == 3 ? 12000 : 144000) );
//???? If computed bitrate is nonsensical, just use header bitrate
if (retInt < 1) {
return getHeaderBitRate();
}
else {
return retInt;
}
}
private int getBitrateIndex() {
return _header >> 12 & 15;
}
/**
* Mp3 Emphasis
* -> "none", "50/15 ms", null, "CCIT J.17"
*
* @see #getEmphasisIndex
* @return java.lang.String string reprensentation of emphasis
*/
public String getEmphasis() {
switch (getEmphasisIndex()) {
case 0:
return "none";
case 1:
return "50/15 ms";
case 2:
return null;
case 3:
return "CCIT J.17";
default: //not an official tag
return "<unknown>";
}
}
private int getEmphasisIndex() {
return _header & 3;
}
/**
* Bytes in the mp3 file
*
* @return long
*/
public long getFileSize() {
if (hasVariableBitRate() && _vbrHeader.numBytes != -1) {
return _vbrHeader.numBytes;
}
return new File(_file).length();
}
private int getFrameSync() {
return _header >> 21 & 2047;
}
/**
* The frequency is dependent on bitrate index and MPEG version
* -> MPEG 2.5 - 32000, 16000, 8000
* -> MPEG 2 - 22050, 24000, 16000
* -> MPEG 1 - 44100, 48000, 32000
*
* @see #getVersionIndex
* @see #getFrequencyIndex
* @return the current frequency [8000-48000 Hz]
*/
public int getFrequency() {
switch (getVersionIndex()) {
case 0: //MPEG 2.5 - 32000, 16000, 8000
switch(getFrequencyIndex()) {
case 0:
return 11025; //!!32000 isn't correct!!
case 1:
return 12000; //!!16000 isn't correct!!
case 2:
return 8000;
default:
return -1;//error
}
case 1: //reserved
return 0;
case 2: //MPEG 2 - 22050, 24000, 16000
switch(getFrequencyIndex()) {
case 0:
return 22050;
case 1:
return 24000;
case 2:
return 16000;
default:
return -1;//error
}
case 3: //MPEG 1 - 44100, 48000, 32000
switch(getFrequencyIndex()) {
case 0:
return 44100;
case 1:
return 48000;
case 2:
return 32000;
default:
return -1;//error
}
default: //error
return -1;
}
}
private int getFrequencyIndex() {
return _header >> 10 & 3;
}
/**
* Based on the bitrate index found in the header
* The header bit rate is based off the BITRATE_TABLE values using indexes
* whereas the other bit rate is calculated directly without the table
* both rates should be equal, excluding possible VBR discrepencies
*
* @see getBitRate
* @return int The bitrate in between 8 - 448 Kb/s .
*/
public int getHeaderBitRate() {
int ind = -1;
switch (getVersionIndex()) {
case 0: //2.0
case 2: //2.5
if( getLayer_Numeric() == 1 ) { // mpeg layer 1
ind = 3;
}
else {// mpeg layer 2 & 3 if( layer == 2 || layer == 3 ) {
ind = 4;
}
break;
case 1: //error or nothing
default:
return 0;
case 3:
ind = getLayer_Numeric()-1;
//if( layer == MPEG_L_1 ) ind = 0;
//else if( layer == MPEG_L_2 ) ind = 1;
//else if( layer == MPEG_L_3 ) ind = 2;
}
//if( bitrateIndex >= 0 && bitrateIndex <= 15 ) {
try {
short[] BITRATE_TABLE = {
0, 0, 0, 0, 0,
32, 32, 32, 32, 8,
64, 48, 40, 48, 16,
96, 56, 48, 56, 24,
128, 64, 56, 64, 32,
160, 80, 64, 80, 40,
192, 96, 80, 96, 48,
224, 112, 96, 112, 56,
256, 128, 112, 128, 64,
288, 160, 128, 144, 80,
320, 192, 160, 160, 96,
352, 224, 192, 176, 112,
384, 256, 224, 192, 128,
416, 320, 256, 224, 144,
448, 384, 320, 256, 160};
return BITRATE_TABLE[getBitrateIndex()*5+ind];
} catch (ArrayIndexOutOfBoundsException aiob) {
return -1;
}
}
/**
* Layer formula:
* 4 - layerIndex
* -> 1, 2, 3
*
* @see #getLayerIndex
* @return int the Layer [1-3] in small int format
*/
public int getLayer_Numeric() {
return 4 - getLayerIndex();
}
/**
* Layer formula:
* 4 - layerIndex
* -> null, "Layer III", "Layer II", "Layer I"
*
* @see #getLayerIndex
* @return java.lang.String representation of Mp3 Layer
*/
public String getLayer_String() {
switch (getLayerIndex()) {
//for those not in the know...don't worry
//the "Layer " string is reused internally bytecode
case 1:
return "Layer " + "III";
case 2:
return "Layer " + "II";
case 3:
return "Layer " + "I";
default:
return "Layer " + "?";
}
}
private int getLayerIndex() {
return _header >> 17 & 3;
}
/**
* Length in seconds formula:
* -> fileSize / (bitrate * 100 / 8)
*
* @see #getFileSize
* @see #getHeaderBitRate
* @return long mp3 seconds
*/
public long getLengthInSeconds() {
if (hasVariableBitRate()) {
int i = _vbrHeader.getLengthInSeconds();
if (i != -1) {
return i;
}
}
return getFileSize() / (getHeaderBitRate()*1000 / 8);
}
/**
* Output channel information
* "Stereo", "Joint Stereo", "Dual Channel", "Single Channel"
*
* @see #getModeIndex
* @return java.lang.String Display representation of playing mode
*/
public String getMode() {
switch(getModeIndex()) {
case 0:
return "Stereo";
case 1:
return "Joint Stereo";
case 2:
return "Dual Channel";
case 3:
return "Single Channel";
default:
return "<unknown>";
}
}
/**
* Mode extension joins information not used for stereo effect, thus
* reducing needed resources. These bits are dynamically determined by an
* encoder in Joint stereo mode. Complete frequency range of MPEG files is
* divided in 32 subbands. For Layer I & II, bits determine frequency range
* where intensity stereo is applied. For Layer III, two bits determine
* whether intensity stereo or m/s stereo is used.
*
* Layer I and II | Layer III
* -----------------------------------------------------
* value | Layer I & II | Intensity stereo | MS stereo
* -----------------------|-----------------------------
* 00 | bands 4 to 31 | off | off
* 01 | bands 8 to 31 | on | off
* 10 | bands 12 to 31 | off | on
* 11 | bands 16 to 31 | on | on
*/
private int getModeExtIndex() {
return _header >> 4 & 3;
}
private int getModeIndex() {
return _header >> 6 & 3;
}
/**
* FrameSize formula
* mpeg v1: FrameSize = 12 * BitRate / SampleRate + Padding
* mpeg v2: FrameSize = 144 * BitRate / SampleRate + Padding
* bitrate is kbps and sample rate in Hz, so multiply BitRate by 1000
* Number of Frames formula
* mp3 file length in bytes / frame size
* the VBR header usually has the number of frames stored internally
*
* !!Results may not be precise as frame calculation is not always exact.
* Programs like Winamp occasionaly return slightly different results.
* For example, we don't exclude added frames like ID3 tags.
*
* @!deprecated Not used internally
* @return int frames calculated from mp3 (possible vbr) header
*/
public int getNumberOfFrames() {
if (hasVariableBitRate() && _vbrHeader.numFrames != -1) {
return _vbrHeader.numFrames;
}
//getHeaderBitRate()
//we round the calculation using (int) which produces a result
//similiar to Winamp stats, but this breaks other calcs elsewhere
return (int)
( getFileSize()
/
( getLayerIndex() == 3 ? 12000 : 144000 * getBitRate()
/
getFrequency() + (isPadded() ? getLayerIndex() == 3 ? 32 : 8 : 0)
)
);
}
/**
* VBR header containing Table of Contents and Quality
*
* @return MP3Info.VBRHeader Variable Bit Rate header
*/
public MP3Info.VBRHeader getVBRHeader() {
return _vbrHeader;
}
/**
* Based on the version index
* -> 2.5, 0.0, 2.0, 1.0
*
* @see #getVersionIndex
* @return double the MPEG version number
*/
public double getVersion_Numeric() {
switch (getVersionIndex()) {
case 0:
return 2.5;
case 1:
default:
return 0.0;
case 2:
return 2.0;
case 3:
return 1.0;
}
}
/**
* Based on the version index
* -> "MPEG Version 2.5", null, "MPEG Version 2.0", "MPEG Version 1.0"
*
* @see #getVersionIndex
* @return java.lang.String representation of version
*/
public String getVersion_String() {
switch (getVersionIndex()) {
//for those not in the know...don't worry
//the "Layer " string is reused internally bytecode
case 0:
return "MPEG Version " + "2.5";
case 1:
return null;
case 2:
return "MPEG Version " + "2.0";
case 3:
return "MPEG Version " + "1.0";
default:
return "MPEG Version " + "?";
}
}
private int getVersionIndex() {
return _header >> 19 & 3;
}
/**
* Whether the bits per frame are not constant
*
* @return True if this file has a VBR
*/
public boolean hasVariableBitRate() {
return _vbrHeader != null;
}
/**
* Whether the copyright bit is flagged in the mp3 header
*
* @return boolean true if flag found
*/
public boolean isCoprighted() {
return (_header >> 3 & 1) != 0;
}
/**
* Whether the original bit is flagged in the mp3 header
*
* @return boolean true if flag found
*/
public boolean isOriginal() {
return (_header >> 2 & 1) != 0;
}
/**
* Whether padding bit is set; Padding is used to fit bit rates exactly.
* :Example: 128k 44.1kHz layer II uses a lot of 418 bytes and some of
* 417 bytes long frames to get the exact 128k bitrate. For Layer I
* slot is 32 bits long, Layer II and Layer III slot is 8 bits long.
*
* @return boolean true if flag found
*/
public boolean isPadded() {
return (_header >> 9 & 1) != 0;
}
/**
* Whether the private bit is flagged in the mp3 header
*
* @return boolean true if flag found
*/
public boolean isPrivate() {
return (_header >> 8 & 1) != 0;
}
/**
* Whether the protection bit is flagged in mp3 header
* Indicates CRC; 16 bit crc follows file header
*
* @return boolean true if flag found
*/
public boolean isProtected() {
//CRC protection is ON when bit is not set
return (_header >> 16 & 1) == 0;
}
/**
* Whether this MP3 is embedded in a WAV file
*
* RIFF(Resource Interchange File Format) is a tagged file structure
* developed for multimedia resource files. The structure of RIFF
* is similar to the structure of an ElectronicArts IFF file. RIFF is
* not actually a file format itself (since it does not represent a
* specific kind of information), but its name contains the words
* `interchange file format' in recognition of its roots in IFF.
*
* ::the beginning of file will start as follows::
* RIFF õY
1 WAVE fmt
* AAAA BBBB CCCC DDDD
*
* A 4 bytes RIFF Tag
* B 4 bytes File Size - Ignored for this test
* C 4 bytes WAVE Tag
* D 4 bytes fmt name
*
* @return boolean true if file is marked as Replay Gain RIFF-WAV
* !!Doesn't gurantee file is a valid or playable RIFF-WAV
*/
public boolean isRiffWav() {
//the results of this test are not persisted on the object
//there's little benefit for a method that may never be used
boolean result = false;
FileInputStream fis = null;
try { //safety
fis = new FileInputStream(_file);
byte[] buffer = new byte[16];
fis.read(buffer);
result =
buffer[ 0] == 'R'
&& buffer[ 1] == 'I'
&& buffer[ 2] == 'F'
&& buffer[ 3] == 'F'
&& buffer[ 8] == 'W'
&& buffer[ 9] == 'A'
&& buffer[10] == 'V'
&& buffer[11] == 'E'
&& buffer[12] == 'f'
&& buffer[13] == 'm'
&& buffer[14] == 't'
&& buffer[15] == ' ';
} catch(IOException ignored) {
// not a riff.
} finally {
if( fis != null ) {
try {
fis.close();
} catch(IOException ioe) {}
}
}
return result;
}
/**
* The LAME tag is not really all that lame
* Added when using the LAME opensource MP3 encoder, the tag provides
* song details, most importatnly for us would be any bit rate info.
*
* ::Example:: LAME Tag
*
* 0005 LAME3.90. õY
a kY
1 õY
q 7Y
3 dY
2 pY
i 0
* ZZZZ AA.....AA BCDD DDEE FFGH IIIJ KLLL LMMN N
*
* Z 4 bytes VBR quality
* the last part of Xing tag, is included in LAME tag
* A 20 bytes LAME Tag
* may not use all 20 bytes; example: 'LAME3.12 (beta 6)'
* B 1 byte LAME Tag revision + VBR method
* no vbr/cbr, abr, vbr-old/vbr-rh, vbr-mtrh, vbr-new/vbr-mt
* C 1 byte Lowpass filter value
* divided by 100
* D 4 bytes Replay Gain
* see http://www.david.robinson.org/replaylevel/
* E 2 bytes Radio Replay Gain
* required to make all tracks equal loudness
* F 2 bytes Audiophile Replay Gain
* required to give ideal listening loudness
* G 1 byte Encoding flags + ATH Type
* --nspsytune, --nssafejoint, --nogap (combination)
* H 1 byte ABR {specified bitrate} or {minimal bitrate}
* if the file is NOT an ABR file then (CBR/VBR)
* I 3 bytes Encoder delays
* samples added at start & padded at end complete last frame
* J 1 byte Misc
* noise shaping, stereo mode, optimal quality, sample freq
* K 1 byte MP3 Gain
* mp3 amplification factor
* L 4 bytes Music Length
* file size minus additional tags
* M 2 bytes Music CRC
* CRC-16 of mp3 music data as made originally by LAME
* N 2 bytes CRC-16 of LAME Tag
* CRC-16 of first 190 bytes of the VBR header frame
*
* @deprecated
*/
/*
private void loadLAMETag (byte buf[], int offset) {
try {
}
catch (Throwable t) {} //bombed trying to build LAME tag
} */
/**
* MPEG files frame bitrates may change in a variable bitrate (VBR). Each
* frame is encoded at a different rate to maximaize quality/file size.
* 1. by bitrate switching: each frame may be created differently.
* 2. by bit reservoir: bits borrowed/given to other frames where needed.
*
* !!NOTE!! All Fhg files encode 160kb into the first mp3 header
*
* ::Example:: Fhg VBR Tag, bytes after header flag are optional flag
*
* VBRI 01949 0212 36-K pS12 0102 j80d 0....1
* AAAA BBBB CCDD DDEE EEFF GGGG HHII I....I
*
* A 4 bytes Header Tag
* "VBRI"
* B 4 bytes Header / Version Flags
* 4 possible flags, determines what data follows (last bit)
*
* OPTIONAL C-G according to flags
* C 2 bytes VBR Scale
* A VBR quality indicator: 0=best 100=worst
* D 4 bytes # of Bytes Per Frame / Stream Size
*
* E 4 bytes MPEG File Frame Size
*
* F 2 bytes Number of seek offsets
*
* G 4 bytes unknown
*
* H 2 bytes offset "stride" (number of frames between offsets)
*
* I F*2 bytes Table of Contents (TOC)
* seek offsets 0-F (from beginning of file)
*
*/
private void loadFhgHeader (byte buf[], int pos) {
_vbrHeader = new MP3Info.VBRHeader();
_vbrHeader.scale = ByteOrder.ubyte2int(buf[pos+=2]);
_vbrHeader.numBytes = ((ByteOrder.ubyte2int(buf[++pos]) << 24)
+ (ByteOrder.ubyte2int(buf[++pos]) << 16)
+ (ByteOrder.ubyte2int(buf[++pos]) << 8)
+ (ByteOrder.ubyte2int(buf[++pos]) ));
_vbrHeader.numFrames =((ByteOrder.ubyte2int(buf[++pos]) << 24)
+ (ByteOrder.ubyte2int(buf[++pos]) << 16)
+ (ByteOrder.ubyte2int(buf[++pos]) << 8)
+ (ByteOrder.ubyte2int(buf[++pos]) ));
/* TOC ignored [format is sketchy]
byte b = (byte)ByteOrder.ubyte2int(buf[pos+=3]);
if((b & (byte)(1 << 2 )) != 0 ) {
_vbrHeader.seek =((ByteOrder.ubyte2int(buf[++pos]) << 8)
+ (ByteOrder.ubyte2int(buf[++pos]) ))
_vbrHeader.toc = new byte[100];
System.arraycopy(buf, ++pos, _vbrHeader.toc, 0, f);
}
*/
}
/**
* MPEG files frame bitrates may change in a variable bitrate (VBR). Each
* frame is encoded at a different rate to maximaize quality/file size.
* 1. by bitrate switching: each frame may be created differently.
* 2. by bit reservoir: bits borrowed/given to other frames where needed.
*
* ::Example:: Xing VBR Tag, bytes after header flag are optional flag
*
* Xing 0007 0254 1236 12...21 0058
* AAAA BBBB CCCC DDDD FF...FF GGGG
*
* A 4 bytes Header Tag
* "Xing" or possibly "FBRI" {"Info" is also possible in CBR}
* B 4 bytes Header Flags
* 4 possible flags, determines what data follows (last bit)
*
* OPTIONAL C-G according to flags
* C 4 bytes MPEG File Frame Size
*
* D 4 bytes # of Bytes Per Frame / Stream Size
*
* F 100 bytes Table of Contents (TOC)
* TOC is a 100-byte array that tells a player how many 256ths
* of the file to jump to find a particular point -in percent.
* :Example: jump to half-way (50%) point in 3,000,000 byte
* file, then look at the 50th entry in the TOC which is 130.
* Seek to 130/256*3000000=1523438th byte, scan to next frame.
* G 4 bytes VBR Scale
* A VBR quality indicator: 0=best 100=worst
*/
private void loadXingHeader (byte buf[], int offset) {
_vbrHeader = new MP3Info.VBRHeader();
byte b = (byte)ByteOrder.ubyte2int(buf[offset+=3]);
if ((b & 1) != 0) {
_vbrHeader.numFrames =((ByteOrder.ubyte2int(buf[++offset]) << 24)
+ (ByteOrder.ubyte2int(buf[++offset]) << 16)
+ (ByteOrder.ubyte2int(buf[++offset]) << 8)
+ (ByteOrder.ubyte2int(buf[++offset]) ));
}
if((b & 2) != 0 ) {
_vbrHeader.numBytes = ((ByteOrder.ubyte2int(buf[++offset]) << 24)
+ (ByteOrder.ubyte2int(buf[++offset]) << 16)
+ (ByteOrder.ubyte2int(buf[++offset]) << 8)
+ (ByteOrder.ubyte2int(buf[++offset]) ));
}
if((b & 4) != 0 ) {
_vbrHeader.toc = new byte[100];
System.arraycopy(buf, ++offset, _vbrHeader.toc, 0, 100);
offset += 99;
}
if((b & 8) != 0 ) {
_vbrHeader.scale = ByteOrder.ubyte2int(buf[offset+=4]);
}
}
}