FileDocCategorySizeDatePackage
MP3File.javaAPI DocJaudiotagger 2.0.430698Mon Sep 12 16:24:46 BST 2011org.jaudiotagger.audio.mp3

MP3File

public class MP3File extends org.jaudiotagger.audio.AudioFile
This class represents a physical MP3 File

Fields Summary
private static final int
MINIMUM_FILESIZE
protected static AbstractTagDisplayFormatter
tagFormatter
private AbstractID3v2Tag
id3v2tag
the ID3v2 tag that this file contains.
private ID3v24Tag
id3v2Asv24tag
Representation of the idv2 tag as a idv24 tag
private org.jaudiotagger.tag.lyrics3.AbstractLyrics3
lyrics3tag
The Lyrics3 tag that this file contains.
private ID3v1Tag
id3v1tag
The ID3v1 tag that this file contains.
public static final int
LOAD_IDV1TAG
public static final int
LOAD_IDV2TAG
public static final int
LOAD_LYRICS3
This option is currently ignored
public static final int
LOAD_ALL
Constructors Summary
public MP3File()
Creates a new empty MP3File datatype that is not associated with a specific file.


                       
     
    
    
public MP3File(File file)
Creates a new MP3File datatype and parse the tag from the given file Object.

param
file MP3 file
throws
IOException on any I/O error
throws
TagException on any exception generated by this library.
throws
org.jaudiotagger.audio.exceptions.ReadOnlyFileException
throws
org.jaudiotagger.audio.exceptions.InvalidAudioFrameException

        this(file, LOAD_ALL);
    
public MP3File(String filename)
Creates a new MP3File datatype and parse the tag from the given filename.

param
filename MP3 file
throws
IOException on any I/O error
throws
TagException on any exception generated by this library.
throws
org.jaudiotagger.audio.exceptions.ReadOnlyFileException
throws
org.jaudiotagger.audio.exceptions.InvalidAudioFrameException

        this(new File(filename));
    
public MP3File(File file, int loadOptions)
Creates a new MP3File datatype and parse the tag from the given file Object, files must be writable to use this constructor.

param
file MP3 file
param
loadOptions decide what tags to load
throws
IOException on any I/O error
throws
TagException on any exception generated by this library.
throws
org.jaudiotagger.audio.exceptions.ReadOnlyFileException
throws
org.jaudiotagger.audio.exceptions.InvalidAudioFrameException


                                                                     
             
    
        this(file, loadOptions, false);
    
public MP3File(File file, int loadOptions, boolean readOnly)
Creates a new MP3File dataType and parse the tag from the given file Object, files can be opened read only if required.

param
file MP3 file
param
loadOptions decide what tags to load
param
readOnly causes the files to be opened readonly
throws
IOException on any I/O error
throws
TagException on any exception generated by this library.
throws
org.jaudiotagger.audio.exceptions.ReadOnlyFileException
throws
org.jaudiotagger.audio.exceptions.InvalidAudioFrameException

        RandomAccessFile newFile = null;
        try
        {
            this.file = file;

            //Check File accessibility
            newFile = checkFilePermissions(file, readOnly);

            //Read ID3v2 tag size (if tag exists) to allow audioHeader parsing to skip over tag
            long startByte = AbstractID3v2Tag.getV2TagSizeIfExists(file);

            //If exception reading Mpeg then we should give up no point continuing
            audioHeader = new MP3AudioHeader(file, startByte);

            if (startByte != ((MP3AudioHeader) audioHeader).getMp3StartByte())
            {
                logger.config("First header found after tag:" + audioHeader);
                audioHeader = checkAudioStart(startByte, (MP3AudioHeader) audioHeader);
            }

            //Read v1 tags (if any)
            readV1Tag(file, newFile, loadOptions);

            //Read v2 tags (if any)
            readV2Tag(file, loadOptions);

            //If we have a v2 tag use that, if we dont but have v1 tag use that
            //otherwise use nothing
            //TODO:if have both should we merge
            //rather than just returning specific ID3v22 tag, would it be better to return v24 version ?
            if (this.getID3v2Tag() != null)
            {
                tag = this.getID3v2Tag();
            }
            else if (id3v1tag != null)
            {
                tag = id3v1tag;
            }

            //Read Lyrics 3
            //readLyrics3Tag(File file,RandomAccessFile  newFile,int loadOptions)
        }
        finally
        {
            if (newFile != null)
            {
                newFile.close();
            }
        }
    
Methods Summary
private MP3AudioHeadercheckAudioStart(long startByte, MP3AudioHeader currentHeader)
Regets the audio header starting from start of file, and write appropriate logging to indicate potential problem to user.

param
startByte
param
currentHeader
return
throws
IOException
throws
InvalidAudioFrameException

        MP3AudioHeader newAudioHeader;
        MP3AudioHeader nextAudioHeader;

        logger.warning(ErrorMessage.MP3_ID3TAG_LENGTH_INCORRECT.getMsg(file.getPath(), Hex.asHex(startByte), Hex.asHex(currentHeader.getMp3StartByte())));

        //because we cant agree on start location we reread the audioheader from the start of the file, at least
        //this way we cant overwrite the audio although we might overwrite part of the tag if we write this file
        //back later
        newAudioHeader = new MP3AudioHeader(file, 0);
        logger.config("Checking from start:" + newAudioHeader);

        if (currentHeader.getMp3StartByte() == newAudioHeader.getMp3StartByte())
        {
            //Although the tag size appears to be incorrect at least we have found the same location for the start
            //of audio whether we start searching from start of file or at the end of the alleged of file so no real
            //problem
            logger.config(ErrorMessage.MP3_START_OF_AUDIO_CONFIRMED.getMsg(file.getPath(), Hex.asHex(newAudioHeader.getMp3StartByte())));
            return currentHeader;
        }
        else
        {
            //We get a different value if read from start, can't guarantee 100% correct lets do some more checks
            logger.config((ErrorMessage.MP3_RECALCULATED_POSSIBLE_START_OF_MP3_AUDIO.getMsg(file.getPath(), Hex.asHex(newAudioHeader.getMp3StartByte()))));

            //Frame counts dont match so eiither currentHeader or newAudioHeader isnt really audio header
            if (currentHeader.getNumberOfFrames() != newAudioHeader.getNumberOfFrames())
            {
                //Skip to the next header (header 2, counting from start of file)
                nextAudioHeader = new MP3AudioHeader(file, newAudioHeader.getMp3StartByte() + newAudioHeader.mp3FrameHeader.getFrameLength());
                logger.config("Checking next:" + nextAudioHeader);

                //It matches the header we found when doing the original search from after the ID3Tag therefore it
                //seems that newAudioHeader was a false match and the original header was correct
                if (nextAudioHeader.getMp3StartByte() == currentHeader.getMp3StartByte())
                {
                    logger.warning((ErrorMessage.MP3_START_OF_AUDIO_CONFIRMED.getMsg(file.getPath(), Hex.asHex(currentHeader.getMp3StartByte()))));
                    return currentHeader;
                }
                //it matches the header we just found so lends weight to the fact that the audio does indeed start at new header
                else if (nextAudioHeader.getNumberOfFrames() == newAudioHeader.getNumberOfFrames())
                {
                    logger.warning((ErrorMessage.MP3_RECALCULATED_START_OF_MP3_AUDIO.getMsg(file.getPath(), Hex.asHex(newAudioHeader.getMp3StartByte()))));
                    return newAudioHeader;
                }
                ///Not sure but safer to return earlier audio beause stops jaudiotagger overwriting when writing tag
                else
                {
                    logger.warning((ErrorMessage.MP3_RECALCULATED_START_OF_MP3_AUDIO.getMsg(file.getPath(), Hex.asHex(newAudioHeader.getMp3StartByte()))));
                    return newAudioHeader;
                }
            }
            //Same frame count so probably both audio headers with newAudioHeader being the firt one
            else
            {
                logger.warning((ErrorMessage.MP3_RECALCULATED_START_OF_MP3_AUDIO.getMsg(file.getPath(), Hex.asHex(newAudioHeader.getMp3StartByte()))));
                return newAudioHeader;
            }
        }
    
public voidcommit()
Overridden for compatibility with merged code

throws
CannotWriteException

        try
        {
            save();
        }
        catch (IOException ioe)
        {
            throw new CannotWriteException(ioe);
        }
        catch (TagException te)
        {
            throw new CannotWriteException(te);
        }
    
public org.jaudiotagger.tag.TagcreateDefaultTag()
Create Default Tag

return

        return new ID3v23Tag();
    
private static voidcreatePlainTextStructureFormatter()

        tagFormatter = new PlainTextTagDisplayFormatter();
    
private static voidcreateXMLStructureFormatter()

        tagFormatter = new XMLTagDisplayFormatter();
    
public voiddelete(AbstractTag mp3tag)
Remove tag from file

param
mp3tag
throws
FileNotFoundException
throws
IOException

        RandomAccessFile raf = new RandomAccessFile(this.file, "rws");
        mp3tag.delete(raf);
        raf.close();
        if(mp3tag instanceof ID3v1Tag)
        {
            id3v1tag=null;
        }

        if(mp3tag instanceof AbstractID3v2Tag)
        {
            id3v2tag=null;
        }
    
public java.lang.StringdisplayStructureAsPlainText()
Displays MP3File Structure

        createPlainTextStructureFormatter();
        tagFormatter.openHeadingElement("file", this.getFile().getAbsolutePath());
        if (this.getID3v1Tag() != null)
        {
            this.getID3v1Tag().createStructure();
        }
        if (this.getID3v2Tag() != null)
        {
            this.getID3v2Tag().createStructure();
        }
        tagFormatter.closeHeadingElement("file");
        return tagFormatter.toString();
    
public java.lang.StringdisplayStructureAsXML()
Displays MP3File Structure

        createXMLStructureFormatter();
        tagFormatter.openHeadingElement("file", this.getFile().getAbsolutePath());
        if (this.getID3v1Tag() != null)
        {
            this.getID3v1Tag().createStructure();
        }
        if (this.getID3v2Tag() != null)
        {
            this.getID3v2Tag().createStructure();
        }
        tagFormatter.closeHeadingElement("file");
        return tagFormatter.toString();
    
public java.io.FileextractID3v2TagDataIntoFile(java.io.File outputFile)
Extracts the raw ID3v2 tag data into a file.

This provides access to the raw data before manipulation, the data is written from the start of the file to the start of the Audio Data. This is primarily useful for manipulating corrupted tags that are not (fully) loaded using the standard methods.

param
outputFile to write the data to
return
throws
TagNotFoundException
throws
IOException

        int startByte = (int) ((MP3AudioHeader) audioHeader).getMp3StartByte();
        if (startByte >= 0)
        {

            //Read byte into buffer
            FileInputStream fis = new FileInputStream(file);
            FileChannel fc = fis.getChannel();
            ByteBuffer bb = ByteBuffer.allocate(startByte);
            fc.read(bb);

            //Write bytes to outputFile
            FileOutputStream out = new FileOutputStream(outputFile);
            out.write(bb.array());
            out.close();
            fc.close();
            fis.close();
            return outputFile;
        }
        throw new TagNotFoundException("There is no ID3v2Tag data in this file");
    
public ID3v1TaggetID3v1Tag()
Returns the ID3v1 tag for this dataType.

return
the ID3v1 tag for this dataType

        return id3v1tag;
    
public AbstractID3v2TaggetID3v2Tag()
Returns the ID3v2 tag for this datatype.

return
the ID3v2 tag for this datatype

        return id3v2tag;
    
public ID3v24TaggetID3v2TagAsv24()

return
a representation of tag as v24

        return id3v2Asv24tag;
    
public MP3AudioHeadergetMP3AudioHeader()
Return audio header

return

        return (MP3AudioHeader) getAudioHeader();
    
public longgetMP3StartByte(java.io.File file)
Used by tags when writing to calculate the location of the music file

param
file
return
the location within the file that the audio starts
throws
java.io.IOException
throws
org.jaudiotagger.audio.exceptions.InvalidAudioFrameException

        try
        {
            //Read ID3v2 tag size (if tag exists) to allow audio header parsing to skip over tag
            long startByte = AbstractID3v2Tag.getV2TagSizeIfExists(file);

            MP3AudioHeader audioHeader = new MP3AudioHeader(file, startByte);
            if (startByte != audioHeader.getMp3StartByte())
            {
                logger.config("First header found after tag:" + audioHeader);
                audioHeader = checkAudioStart(startByte, audioHeader);
            }
            return audioHeader.getMp3StartByte();
        }
        catch (InvalidAudioFrameException iafe)
        {
            throw iafe;
        }
        catch (IOException ioe)
        {
            throw ioe;
        }
    
public static AbstractTagDisplayFormattergetStructureFormatter()

        return tagFormatter;
    
public booleanhasID3v1Tag()
Returns true if this datatype contains an Id3v1 tag

return
true if this datatype contains an Id3v1 tag

        return (id3v1tag != null);
    
public booleanhasID3v2Tag()
Returns true if this datatype contains an Id3v2 tag

return
true if this datatype contains an Id3v2 tag

        return (id3v2tag != null);
    
public voidprecheck(java.io.File file)
Check can write to file

param
file
throws
IOException

        if (!file.exists())
        {
            logger.severe(ErrorMessage.GENERAL_WRITE_FAILED_BECAUSE_FILE_NOT_FOUND.getMsg(file.getName()));
            throw new IOException(ErrorMessage.GENERAL_WRITE_FAILED_BECAUSE_FILE_NOT_FOUND.getMsg(file.getName()));
        }

        if (!file.canWrite())
        {
            logger.severe(ErrorMessage.GENERAL_WRITE_FAILED.getMsg(file.getName()));
            throw new IOException(ErrorMessage.GENERAL_WRITE_FAILED.getMsg(file.getName()));
        }

        if (file.length() <= MINIMUM_FILESIZE)
        {
            logger.severe(ErrorMessage.GENERAL_WRITE_FAILED_BECAUSE_FILE_IS_TOO_SMALL.getMsg(file.getName()));
            throw new IOException(ErrorMessage.GENERAL_WRITE_FAILED_BECAUSE_FILE_IS_TOO_SMALL.getMsg(file.getName()));
        }
    
private voidreadLyrics3Tag(java.io.File file, java.io.RandomAccessFile newFile, int loadOptions)
Read lyrics3 Tag

TODO:not working

param
file
param
newFile
param
loadOptions
throws
IOException

        /*if ((loadOptions & LOAD_LYRICS3) != 0)
        {
            try
            {
                lyrics3tag = new Lyrics3v2(newFile);
            }
            catch (TagNotFoundException ex)
            {
            }
            try
            {
                if (lyrics3tag == null)
                {
                    lyrics3tag = new Lyrics3v1(newFile);
                }
            }
            catch (TagNotFoundException ex)
            {
            }
        }
        */
    
private voidreadV1Tag(java.io.File file, java.io.RandomAccessFile newFile, int loadOptions)
Read v1 tag

param
file
param
newFile
param
loadOptions
throws
IOException

        if ((loadOptions & LOAD_IDV1TAG) != 0)
        {
            logger.finer("Attempting to read id3v1tags");
            try
            {
                id3v1tag = new ID3v11Tag(newFile, file.getName());
            }
            catch (TagNotFoundException ex)
            {
                logger.config("No ids3v11 tag found");
            }

            try
            {
                if (id3v1tag == null)
                {
                    id3v1tag = new ID3v1Tag(newFile, file.getName());
                }
            }
            catch (TagNotFoundException ex)
            {
                logger.config("No id3v1 tag found");
            }
        }
    
private voidreadV2Tag(java.io.File file, int loadOptions)
Read V2tag if exists

TODO:shouldnt we be handing TagExceptions:when will they be thrown

param
file
param
loadOptions
throws
IOException
throws
TagException

        //We know where the Actual Audio starts so load all the file from start to that point into
        //a buffer then we can read the IDv2 information without needing any more file I/O
        int startByte = (int) ((MP3AudioHeader) audioHeader).getMp3StartByte();
        if (startByte >= AbstractID3v2Tag.TAG_HEADER_LENGTH)
        {
            logger.finer("Attempting to read id3v2tags");
            FileInputStream fis = null;
            FileChannel fc = null;
            ByteBuffer bb = null;
            try
            {
                fis = new FileInputStream(file);
                fc = fis.getChannel();
                //Read into Byte Buffer
                bb = ByteBuffer.allocate(startByte);
                fc.read(bb);
            }
            finally
            {
                if (fc != null)
                {
                    fc.close();
                }

                if (fis != null)
                {
                    fis.close();
                }
            }

            bb.rewind();

            if ((loadOptions & LOAD_IDV2TAG) != 0)
            {
                logger.config("Attempting to read id3v2tags");
                try
                {
                    this.setID3v2Tag(new ID3v24Tag(bb, file.getName()));
                }
                catch (TagNotFoundException ex)
                {
                    logger.config("No id3v24 tag found");
                }

                try
                {
                    if (id3v2tag == null)
                    {
                        this.setID3v2Tag(new ID3v23Tag(bb, file.getName()));
                    }
                }
                catch (TagNotFoundException ex)
                {
                    logger.config("No id3v23 tag found");
                }

                try
                {
                    if (id3v2tag == null)
                    {
                        this.setID3v2Tag(new ID3v22Tag(bb, file.getName()));
                    }
                }
                catch (TagNotFoundException ex)
                {
                    logger.config("No id3v22 tag found");
                }
            }
        }
        else
        {
            logger.config("Not enough room for valid id3v2 tag:" + startByte);
        }
    
public voidsave()
Saves the tags in this dataType to the file referred to by this dataType.

throws
IOException on any I/O error
throws
TagException on any exception generated by this library.

        save(this.file);
    
public voidsave(java.io.File fileToSave)
Saves the tags in this dataType to the file argument. It will be saved as TagConstants.MP3_FILE_SAVE_WRITE

param
fileToSave file to save the this dataTypes tags to
throws
FileNotFoundException if unable to find file
throws
IOException on any I/O error

        //Ensure we are dealing with absolute filepqaths not relative ones
        File file = fileToSave.getAbsoluteFile();

        logger.config("Saving  : " + file.getPath());

        //Checks before starting write
        precheck(file);

        RandomAccessFile rfile = null;
        try
        {
            //ID3v2 Tag
            if (TagOptionSingleton.getInstance().isId3v2Save())
            {
                if (id3v2tag == null)
                {
                    rfile = new RandomAccessFile(file, "rws");
                    (new ID3v24Tag()).delete(rfile);
                    (new ID3v23Tag()).delete(rfile);
                    (new ID3v22Tag()).delete(rfile);
                    logger.config("Deleting ID3v2 tag:"+file.getName());
                    rfile.close();
                }
                else
                {
                    logger.config("Writing ID3v2 tag:"+file.getName());
                    id3v2tag.write(file, ((MP3AudioHeader) this.getAudioHeader()).getMp3StartByte());
                }
            }
            rfile = new RandomAccessFile(file, "rws");

            //Lyrics 3 Tag
            if (TagOptionSingleton.getInstance().isLyrics3Save())
            {
                if (lyrics3tag != null)
                {
                    lyrics3tag.write(rfile);
                }
            }
            //ID3v1 tag
            if (TagOptionSingleton.getInstance().isId3v1Save())
            {
                logger.config("Processing ID3v1");
                if (id3v1tag == null)
                {
                    logger.config("Deleting ID3v1");
                    (new ID3v1Tag()).delete(rfile);
                }
                else
                {
                    logger.config("Saving ID3v1");
                    id3v1tag.write(rfile);
                }
            }
        }
        catch (FileNotFoundException ex)
        {
            logger.log(Level.SEVERE, ErrorMessage.GENERAL_WRITE_FAILED_BECAUSE_FILE_NOT_FOUND.getMsg(file.getName()), ex);
            throw ex;
        }
        catch (IOException iex)
        {
            logger.log(Level.SEVERE, ErrorMessage.GENERAL_WRITE_FAILED_BECAUSE.getMsg(file.getName(), iex.getMessage()), iex);
            throw iex;
        }
        catch (RuntimeException re)
        {
            logger.log(Level.SEVERE, ErrorMessage.GENERAL_WRITE_FAILED_BECAUSE.getMsg(file.getName(), re.getMessage()), re);
            throw re;
        }
        finally
        {
            if (rfile != null)
            {
                rfile.close();
            }
        }
    
public voidsetID3v1Tag(ID3v1Tag id3v1tag)
Sets the ID3v1(_1)tag to the tag provided as an argument.

param
id3v1tag

        logger.config("setting tagv1:v1 tag");
        this.id3v1tag = id3v1tag;
    
public voidsetID3v1Tag(org.jaudiotagger.tag.Tag id3v1tag)

        logger.config("setting tagv1:v1 tag");
        this.id3v1tag = (ID3v1Tag) id3v1tag;
    
public voidsetID3v1Tag(AbstractTag mp3tag)
Sets the ID3v1 tag for this dataType. A new ID3v1_1 dataType is created from the argument and then used here.

param
mp3tag Any MP3Tag dataType can be used and will be converted into a new ID3v1_1 dataType.

        logger.config("setting tagv1:abstract");
        id3v1tag = new ID3v11Tag(mp3tag);
    
public voidsetID3v2Tag(AbstractTag mp3tag)
Sets the ID3v2 tag for this dataType. A new ID3v2_4 dataType is created from the argument and then used here.

param
mp3tag Any MP3Tag dataType can be used and will be converted into a new ID3v2_4 dataType.

        id3v2tag = new ID3v24Tag(mp3tag);

    
public voidsetID3v2Tag(AbstractID3v2Tag id3v2tag)
Sets the v2 tag to the v2 tag provided as an argument. Also store a v24 version of tag as v24 is the interface to be used when talking with client applications.

param
id3v2tag

        this.id3v2tag = id3v2tag;
        if (id3v2tag instanceof ID3v24Tag)
        {
            this.id3v2Asv24tag = (ID3v24Tag) this.id3v2tag;
        }
        else
        {
            this.id3v2Asv24tag = new ID3v24Tag(id3v2tag);
        }
    
public voidsetID3v2TagOnly(AbstractID3v2Tag id3v2tag)
Set v2 tag ,don't need to set v24 tag because saving

param
id3v2tag

        this.id3v2tag = id3v2tag;
        this.id3v2Asv24tag = null;
    
public voidsetTag(org.jaudiotagger.tag.Tag tag)
Set the Tag

If the parameter tag is a v1tag then the v1 tag is set if v2tag then the v2tag.

param
tag

        this.tag = tag;
        if (tag instanceof ID3v1Tag)
        {
            setID3v1Tag((ID3v1Tag) tag);
        }
        else
        {
            setID3v2Tag((AbstractID3v2Tag) tag);
        }