Methods Summary |
---|
public java.lang.String | getBitRate()
if (mp3XingFrame != null && mp3XingFrame.isVbr())
{
return isVbrIdentifier + String.valueOf(bitrate);
}
else if (mp3VbriFrame != null)
{
return isVbrIdentifier + String.valueOf(bitrate);
}
else
{
return String.valueOf(bitrate);
}
|
public long | getBitRateAsNumber()
return bitrate;
|
public java.lang.String | getChannels()
return mp3FrameHeader.getChannelModeAsString();
|
public java.lang.String | getEmphasis()
return mp3FrameHeader.getEmphasisAsString();
|
public java.lang.String | getEncoder()
return encoder;
|
public java.lang.String | getEncodingType()
return TYPE_MP3;
|
public java.lang.String | getFormat()
return mp3FrameHeader.getVersionAsString() + " " + mp3FrameHeader.getLayerAsString();
|
public long | getMp3StartByte()Returns the byte position of the first MP3 Frame that the
file arguement refers to. This is the first byte of music
data and not the ID3 Tag Frame.
return startByte;
|
public java.lang.String | getMpegLayer()
return mp3FrameHeader.getLayerAsString();
|
public java.lang.String | getMpegVersion()
return mp3FrameHeader.getVersionAsString();
|
public long | getNumberOfFrames()
return numberOfFrames;
|
public long | getNumberOfFramesEstimate()
return numberOfFramesEstimate;
|
public double | getPreciseTrackLength()
return trackLength;
|
public java.lang.String | getSampleRate()
return String.valueOf(mp3FrameHeader.getSamplingRate());
|
public int | getSampleRateAsNumber()
return mp3FrameHeader.getSamplingRate();
|
private double | getTimePerFrame()
return timePerFrame;
|
public int | getTrackLength()
return (int) getPreciseTrackLength();
|
public java.lang.String | getTrackLengthAsString()Return the length in user friendly format
final Date timeIn;
try
{
final long lengthInSecs = getTrackLength();
synchronized(timeInFormat)
{
timeIn = timeInFormat.parse(String.valueOf(lengthInSecs));
}
if (lengthInSecs < NO_SECONDS_IN_HOUR)
{
synchronized(timeOutFormat)
{
return timeOutFormat.format(timeIn);
}
}
else
{
synchronized(timeOutOverAnHourFormat)
{
return timeOutOverAnHourFormat.format(timeIn);
}
}
}
catch (ParseException pe)
{
logger.warning("Unable to parse:"+getPreciseTrackLength() +" failed with ParseException:"+pe.getMessage());
return "";
}
|
public boolean | isCopyrighted()
return mp3FrameHeader.isCopyrighted();
|
private boolean | isNextFrameValid(java.io.File seekFile, long filePointerCount, java.nio.ByteBuffer bb, java.nio.channels.FileChannel fc)Called in some circumstances to check the next frame to ensure we have the correct audio header
if (MP3AudioHeader.logger.isLoggable(Level.FINEST))
{
MP3AudioHeader.logger.finer("Checking next frame" + seekFile.getName() + ":fpc:" + filePointerCount + "skipping to:" + (filePointerCount + mp3FrameHeader.getFrameLength()));
}
boolean result = false;
int currentPosition = bb.position();
//Our buffer is not large enough to fit in the whole of this frame, something must
//have gone wrong because frames are not this large, so just return false
//bad frame header
if (mp3FrameHeader.getFrameLength() > (FILE_BUFFER_SIZE - MIN_BUFFER_REMAINING_REQUIRED))
{
MP3AudioHeader.logger.finer("Frame size is too large to be a frame:" + mp3FrameHeader.getFrameLength());
return false;
}
//Check for end of buffer if not enough room get some more
if (bb.remaining() <= MIN_BUFFER_REMAINING_REQUIRED + mp3FrameHeader.getFrameLength())
{
MP3AudioHeader.logger.finer("Buffer too small, need to reload, buffer size:" + bb.remaining());
bb.clear();
fc.position(filePointerCount);
fc.read(bb, fc.position());
bb.flip();
//So now original buffer has been replaced, so set current position to start of buffer
currentPosition = 0;
//Not enough left
if (bb.limit() <= MIN_BUFFER_REMAINING_REQUIRED)
{
//No mp3 exists
MP3AudioHeader.logger.finer("Nearly at end of file, no header found:");
return false;
}
//Still Not enough left for next alleged frame size so giving up
if (bb.limit() <= MIN_BUFFER_REMAINING_REQUIRED + mp3FrameHeader.getFrameLength())
{
//No mp3 exists
MP3AudioHeader.logger.finer("Nearly at end of file, no room for next frame, no header found:");
return false;
}
}
//Position bb to the start of the alleged next frame
bb.position(bb.position() + mp3FrameHeader.getFrameLength());
if (MPEGFrameHeader.isMPEGFrame(bb))
{
try
{
MPEGFrameHeader.parseMPEGHeader(bb);
MP3AudioHeader.logger.finer("Check next frame confirms is an audio header ");
result = true;
}
catch (InvalidAudioFrameException ex)
{
MP3AudioHeader.logger.finer("Check next frame has identified this is not an audio header");
result = false;
}
}
else
{
MP3AudioHeader.logger.finer("isMPEGFrame has identified this is not an audio header");
}
//Set back to the start of the previous frame
bb.position(currentPosition);
return result;
|
public boolean | isOriginal()
return mp3FrameHeader.isOriginal();
|
public boolean | isPadding()
return mp3FrameHeader.isPadding();
|
public boolean | isPrivate()
return mp3FrameHeader.isPrivate();
|
public boolean | isProtected()
return mp3FrameHeader.isProtected();
|
public boolean | isVariableBitRate()
if (mp3XingFrame != null)
{
return mp3XingFrame.isVbr();
}
else if (mp3VbriFrame != null)
{
return mp3VbriFrame.isVbr();
}
else
{
return mp3FrameHeader.isVariableBitRate();
}
|
public boolean | seek(java.io.File seekFile, long startByte)Returns true if the first MP3 frame can be found for the MP3 file
This is the first byte of music data and not the ID3 Tag Frame. *
//References to Xing/VRbi Header
ByteBuffer header;
//This is substantially faster than updating the filechannels position
long filePointerCount;
final FileInputStream fis = new FileInputStream(seekFile);
final FileChannel fc = fis.getChannel();
//Read into Byte Buffer in Chunks
ByteBuffer bb = ByteBuffer.allocateDirect(FILE_BUFFER_SIZE);
//Move FileChannel to the starting position (skipping over tag if any)
fc.position(startByte);
//Update filePointerCount
filePointerCount = startByte;
//Read from here into the byte buffer , doesn't move location of filepointer
fc.read(bb, startByte);
bb.flip();
boolean syncFound = false;
try
{
do
{
//TODO remaining() is quite an expensive operation, isn't there a way we can work this out without
//interrogating the bytebuffer. Also this is rarely going to be true, and could be made less true
//by increasing FILE_BUFFER_SIZE
if (bb.remaining() <= MIN_BUFFER_REMAINING_REQUIRED)
{
bb.clear();
fc.position(filePointerCount);
fc.read(bb, fc.position());
bb.flip();
if (bb.limit() <= MIN_BUFFER_REMAINING_REQUIRED)
{
//No mp3 exists
return false;
}
}
//MP3File.logger.finest("fc:"+fc.position() + "bb"+bb.position());
if (MPEGFrameHeader.isMPEGFrame(bb))
{
try
{
if (MP3AudioHeader.logger.isLoggable(Level.FINEST))
{
MP3AudioHeader.logger.finest("Found Possible header at:" + filePointerCount);
}
mp3FrameHeader = MPEGFrameHeader.parseMPEGHeader(bb);
syncFound = true;
//if(2==1) use this line when you want to test getting the next frame without using xing
if ((header = XingFrame.isXingFrame(bb, mp3FrameHeader))!=null)
{
if (MP3AudioHeader.logger.isLoggable(Level.FINEST))
{
MP3AudioHeader.logger.finest("Found Possible XingHeader");
}
try
{
//Parses Xing frame without modifying position of main buffer
mp3XingFrame = XingFrame.parseXingFrame(header);
}
catch (InvalidAudioFrameException ex)
{
// We Ignore because even if Xing Header is corrupted
//doesn't mean file is corrupted
}
break;
}
else if ((header = VbriFrame.isVbriFrame(bb, mp3FrameHeader))!=null)
{
if (MP3AudioHeader.logger.isLoggable(Level.FINEST))
{
MP3AudioHeader.logger.finest("Found Possible VbriHeader");
}
try
{
//Parses Vbri frame without modifying position of main buffer
mp3VbriFrame = VbriFrame.parseVBRIFrame(header);
}
catch (InvalidAudioFrameException ex)
{
// We Ignore because even if Vbri Header is corrupted
//doesn't mean file is corrupted
}
break;
}
// There is a small but real chance that an unsynchronised ID3 Frame could fool the MPEG
// Parser into thinking it was an MPEG Header. If this happens the chances of the next bytes
// forming a Xing frame header are very remote. On the basis that most files these days have
// Xing headers we do an additional check for when an apparent frame header has been found
// but is not followed by a Xing Header:We check the next header this wont impose a large
// overhead because wont apply to most Mpegs anyway ( Most likely to occur if audio
// has an APIC frame which should have been unsynchronised but has not been) , or if the frame
// has been encoded with as Unicode LE because these have a BOM of 0xFF 0xFE
else
{
syncFound = isNextFrameValid(seekFile, filePointerCount, bb, fc);
if (syncFound)
{
break;
}
}
}
catch (InvalidAudioFrameException ex)
{
// We Ignore because likely to be incorrect sync bits ,
// will just continue in loop
}
}
//TODO position() is quite an expensive operation, isn't there a way we can work this out without
//interrogating the bytebuffer
bb.position(bb.position() + 1);
filePointerCount++;
}
while (!syncFound);
}
catch (EOFException ex)
{
MP3AudioHeader.logger.log(Level.WARNING, "Reached end of file without finding sync match", ex);
syncFound = false;
}
catch (IOException iox)
{
MP3AudioHeader.logger.log(Level.SEVERE, "IOException occurred whilst trying to find sync", iox);
syncFound = false;
throw iox;
}
finally
{
if (fc != null)
{
fc.close();
}
if (fis != null)
{
fis.close();
}
}
//Return to start of audio header
if (MP3AudioHeader.logger.isLoggable(Level.FINEST))
{
MP3AudioHeader.logger.finer("Return found matching mp3 header starting at" + filePointerCount);
}
setFileSize(seekFile.length());
setMp3StartByte(filePointerCount);
setTimePerFrame();
setNumberOfFrames();
setTrackLength();
setBitRate();
setEncoder();
/*if((filePointerCount - startByte )>0)
{
logger.severe(seekFile.getName()+"length:"+startByte+"Difference:"+(filePointerCount - startByte));
}
*/
return syncFound;
|
protected void | setBitRate()Set bitrate in kbps, if Vbr use Xingheader if possible
if (mp3XingFrame != null && mp3XingFrame.isVbr())
{
if (mp3XingFrame.isAudioSizeEnabled() && mp3XingFrame.getAudioSize() > 0)
{
bitrate = (long) ((mp3XingFrame.getAudioSize() * CONVERTS_BYTE_TO_BITS) / (timePerFrame * getNumberOfFrames() * CONVERT_TO_KILOBITS));
}
else
{
bitrate = (long) (((fileSize - startByte) * CONVERTS_BYTE_TO_BITS) / (timePerFrame * getNumberOfFrames() * CONVERT_TO_KILOBITS));
}
}
else if (mp3VbriFrame != null)
{
if (mp3VbriFrame.getAudioSize() > 0)
{
bitrate = (long) ((mp3VbriFrame.getAudioSize() * CONVERTS_BYTE_TO_BITS) / (timePerFrame * getNumberOfFrames() * CONVERT_TO_KILOBITS));
}
else
{
bitrate = (long) (((fileSize - startByte) * CONVERTS_BYTE_TO_BITS) / (timePerFrame * getNumberOfFrames() * CONVERT_TO_KILOBITS));
}
}
else
{
bitrate = mp3FrameHeader.getBitRate();
}
|
protected void | setEncoder()
if (mp3XingFrame != null)
{
if (mp3XingFrame.getLameFrame() != null)
{
encoder = mp3XingFrame.getLameFrame().getEncoder();
}
}
else if (mp3VbriFrame != null)
{
encoder = mp3VbriFrame.getEncoder();
}
|
protected void | setFileSize(long fileSize)Set the size of the file, required in some calculations
this.fileSize = fileSize;
|
protected void | setMp3StartByte(long startByte)Set the location of where the Audio file begins in the file
this.startByte = startByte;
|
protected void | setNumberOfFrames()Set number of frames in this file, use Xing if exists otherwise ((File Size - Non Audio Part)/Frame Size)
numberOfFramesEstimate = (fileSize - startByte) / mp3FrameHeader.getFrameLength();
if (mp3XingFrame != null && mp3XingFrame.isFrameCountEnabled())
{
numberOfFrames = mp3XingFrame.getFrameCount();
}
else if (mp3VbriFrame != null)
{
numberOfFrames = mp3VbriFrame.getFrameCount();
}
else
{
numberOfFrames = numberOfFramesEstimate;
}
|
protected void | setTimePerFrame()Set the time each frame contributes to the audio in fractions of seconds, the higher
the sampling rate the shorter the audio segment provided by the frame,
the number of samples is fixed by the MPEG Version and Layer
timePerFrame = mp3FrameHeader.getNoOfSamples() / mp3FrameHeader.getSamplingRate().doubleValue();
|
protected void | setTrackLength()Estimate the length of the audio track in seconds
Calculation is Number of frames multiplied by the Time Per Frame using the first frame as a prototype
Time Per Frame is the number of samples in the frame (which is defined by the MPEGVersion/Layer combination)
divided by the sampling rate, i.e the higher the sampling rate the shorter the audio represented by the frame is going
to be.
trackLength = numberOfFrames * getTimePerFrame();
|
public java.lang.String | toString()
String s = "fileSize:" + fileSize + " encoder:" + encoder + " startByte:" + Hex.asHex(startByte) + " numberOfFrames:" + numberOfFrames + " numberOfFramesEst:" + numberOfFramesEstimate + " timePerFrame:" + timePerFrame + " bitrate:" + bitrate + " trackLength:" + getTrackLengthAsString();
if (this.mp3FrameHeader != null)
{
s += mp3FrameHeader.toString();
}
if (this.mp3XingFrame != null)
{
s += mp3XingFrame.toString();
}
return s;
|