FileDocCategorySizeDatePackage
MP3File.javaAPI Docjid3 0.4626991Tue Nov 22 02:09:14 GMT 2005org.blinkenlights.jid3

MP3File

public class MP3File extends MediaFile
author
paul A class representing an MP3 file.

Fields Summary
Constructors Summary
public MP3File(File oSourceFile)
Construct an object representing the MP3 file specified.

param
oSourceFile a File pointing to the source MP3 file

        super(oSourceFile);
    
public MP3File(IFileSource oFileSource)

        super(oFileSource);
    
Methods Summary
public ID3V1TaggetID3V1Tag()

        try
        {
            InputStream oSourceIS = new BufferedInputStream(m_oFileSource.getInputStream());

            try
            {
                // copy over all of the file up to the last 128 bytes
                long lFileLength = m_oFileSource.length();
                oSourceIS.skip(lFileLength - 128);

                // check if V1 tag is present
                byte[] abyCheckTag = new byte[3];
                oSourceIS.read(abyCheckTag);
                if ((abyCheckTag[0] == 'T") && (abyCheckTag[1] == 'A") && (abyCheckTag[2] == 'G"))
                {
                    // there is a tag, we must read it
                    ID3V1Tag oID3V1Tag = ID3V1Tag.read(oSourceIS);

                    return oID3V1Tag;
                }
                else
                {
                    return null;
                }
            }
            finally
            {
                oSourceIS.close();
            }
        }
        catch (Exception e)
        {
            throw new ID3Exception(e);
        }
    
public ID3V2TaggetID3V2Tag()

        //TODO: We're only checking for v2.3.0 tags here now.  We'd otherwise have to find
        //      the "ID3" identifier in the file first.
        try
        {
            InputStream oSourceIS = new BufferedInputStream(m_oFileSource.getInputStream());
            ID3DataInputStream oSourceID3DIS = new ID3DataInputStream(oSourceIS);

            try
            {
                // check if v2 tag is present
                byte[] abyCheckTag = new byte[3];
                oSourceID3DIS.readFully(abyCheckTag);
                if ((abyCheckTag[0] == 'I") && (abyCheckTag[1] == 'D") && (abyCheckTag[2] == '3"))
                {
                    return ID3V2Tag.read(oSourceID3DIS);
                }
                else
                {
                    return null;
                }
            }
            finally
            {
                oSourceID3DIS.close();
            }
        }
        catch (ID3Exception e)
        {
            throw e;
        }
        catch (Exception e)
        {
            throw new ID3Exception("Error reading tags from file.", e);
        }
    
public ID3Tag[]getTags()

        List oID3TagList = new ArrayList();
        
        // get ID3V1Tag if they exist
        ID3V1Tag oID3V1Tag = getID3V1Tag();
        if (oID3V1Tag != null)
        {
            oID3TagList.add(oID3V1Tag);
        }
        
        // get ID3V2Tag if they exist
        ID3V2Tag oID3V2Tag = getID3V2Tag();
        if (oID3V2Tag != null)
        {
            oID3TagList.add(oID3V2Tag);
        }
        
        return (ID3Tag[])oID3TagList.toArray(new ID3Tag[0]);
    
public voidremoveID3V1Tag()

        IFileSource oTmpFileSource = null;
        InputStream oSourceIS = null;
        OutputStream oTmpOS = null;

        try
        {
            // open source file for reading
            try
            {
                oSourceIS = new BufferedInputStream(m_oFileSource.getInputStream());
            }
            catch (Exception e)
            {
                throw new ID3Exception("Error opening [" + m_oFileSource.getName() + "]", e);
            }

            try
            {
                // create temporary file to work with
                try
                {
                    oTmpFileSource = m_oFileSource.createTempFile("id3.", ".tmp");
                }
                catch (Exception e)
                {
                    throw new ID3Exception("Unable to create temporary file.", e);
                }

                // open temp file for writing
                try
                {
                    oTmpOS = new BufferedOutputStream(oTmpFileSource.getOutputStream());
                }
                catch (Exception e)
                {
                    throw new ID3Exception("Error opening temporary file for writing.", e);
                }

                try
                {
                    // copy over all of the source file up to but not including the V1 tags,
                    // if they are present
                    long lFileLength = m_oFileSource.length();
                    // copy over all of the file up to the last 128 bytes (in 64k blocks for speed, while remaining memory efficient)
                    byte[] abyBuffer = new byte[65536];
                    long lCopied = 0;
                    long lTotalToCopy = lFileLength - 128;
                    while (lCopied < lTotalToCopy)
                    {
                        long lLeftToCopy = lTotalToCopy - lCopied;
                        long lToCopyNow = (lLeftToCopy >= 65536) ? 65536 : lLeftToCopy;
                        oSourceIS.read(abyBuffer, 0, (int)lToCopyNow);
                        oTmpOS.write(abyBuffer, 0, (int)lToCopyNow);
                        lCopied += lToCopyNow;
                    }

                    // check next three bytes of source file which indicate whether this file already
                    // has a V1 tag on it or not
                    byte[] abyCheckTag = new byte[3];
                    oSourceIS.read(abyCheckTag);
                    if ( ! ((abyCheckTag[0] == 'T") && (abyCheckTag[1] == 'A") && (abyCheckTag[2] == 'G")))
                    {
                        // no V1 tag on this file... copy the rest of it over (3 + 125 = 128 bytes)
                        oTmpOS.write(abyCheckTag);
                        for (int i=0; i < 125; i++)
                        {
                            oTmpOS.write(oSourceIS.read());
                        }
                    }

                    // we're done
                    oTmpOS.flush();
                }
                finally
                {
                    oTmpOS.close();
                }
            }
            finally
            {
                oSourceIS.close();
            }
            
            // move temp file to original source file
            if (! m_oFileSource.delete())
            {
                //HACK:  This is a hack, to get around the fact that at least some JVMs are buggy, in that files which
                //       have been closed are hung onto, pending garbage collection.  By suggesting garbage collection,
                //       the next time, the delete -magically- works.
                int iFails = 1;
                int iDelay = 1;
                while (!m_oFileSource.delete())
                {
                    System.gc();    // this will close the open file
                    Thread.sleep(iDelay);
                    iFails++;
                    iDelay *= 2;
                    if (iFails > 10)
                    {
                        throw new ID3Exception("Unable to delete original file.");
                    }
                }
            }
            if (! oTmpFileSource.renameTo(m_oFileSource))
            {
                throw new ID3Exception("Unable to rename temporary file " + oTmpFileSource.toString() + " to " + m_oFileSource.toString() + ".");
            }
        }
        catch (ID3Exception e)
        {
            throw e;
        }
        catch (Exception e)
        {
            throw new ID3Exception("Error processing [" + m_oFileSource.getName() + "].", e);
        }
    
public voidremoveID3V2Tag()

        IFileSource oTmpFileSource = null;
        InputStream oSourceIS = null;
        OutputStream oTmpOS = null;
        
        // create temporary file to work with
        try
        {
            oTmpFileSource = m_oFileSource.createTempFile("id3.", ".tmp");
        }
        catch (Exception e)
        {
            throw new ID3Exception("Unable to create temporary file.", e);
        }
        
        try
        {
            // open source file for reading
            try
            {
                oSourceIS = new BufferedInputStream(m_oFileSource.getInputStream());
            }
            catch (Exception e)
            {
                throw new ID3Exception("Error opening [" + m_oFileSource.getName() + "]", e);
            }

            try
            {
                // open temp file for writing
                try
                {
                    oTmpOS = new BufferedOutputStream(oTmpFileSource.getOutputStream());
                }
                catch (Exception e)
                {
                    throw new ID3Exception("Error opening temporary file for writing.", e);
                }

                try
                {
                    // copy over the MP3 data from the source file to the temp file
                    // (need to check if there is a V2 tag in the source file, and skip over them if they exist)
                    byte[] abyCheckTag = new byte[3];
                    oSourceIS.read(abyCheckTag);
                    if ((abyCheckTag[0] == 'I") && (abyCheckTag[1] == 'D") && (abyCheckTag[2] == '3"))
                    {
                        // there is a tag in this file.. skip over it
                        // read version information
                        int iVersion = oSourceIS.read();
                        int iPatch = oSourceIS.read();
                        // skip flags
                        oSourceIS.skip(1);
                        // get tag length
                        byte[] abyTagLength = new byte[4];
                        if (oSourceIS.read(abyTagLength) != 4)
                        {
                            throw new ID3Exception("Error reading existing ID3 tags.");
                        }
                        ID3DataInputStream oID3DIS = new ID3DataInputStream(new ByteArrayInputStream(abyTagLength));
                        long iTagLength = oID3DIS.readID3Four();
                        oID3DIS.close();
                        while (iTagLength > 0)
                        {
                            long iNumSkipped = oSourceIS.skip(iTagLength);
                            if (iNumSkipped == 0)
                            {
                                throw new ID3Exception("Error reading existing ID3 tag.");
                            }
                            iTagLength -= iNumSkipped;
                        }
                    }
                    else
                    {
                        // there are no tags in this file...
                        oTmpOS.write(abyCheckTag);
                    }

                    // copy over rest of the file
                    byte[] abyBuffer = new byte[65536];
                    int iNumRead;
                    while ((iNumRead = oSourceIS.read(abyBuffer)) != -1)
                    {
                        oTmpOS.write(abyBuffer, 0, iNumRead);
                    }

                    // we're done
                    oTmpOS.flush();
                }
                finally
                {
                    oTmpOS.close();
                }
            }
            finally
            {
                oSourceIS.close();
            }
            
            // move temp file to original source file
            if (! m_oFileSource.delete())
            {
                //HACK:  This is a hack, to get around the fact that at least some JVMs are buggy, in that files which
                //       have been closed are hung onto, pending garbage collection.  By suggesting garbage collection,
                //       the next time, the delete -magically- works.
                int iFails = 1;
                int iDelay = 1;
                while (!m_oFileSource.delete())
                {
                    System.gc();    // this will close the open file
                    Thread.sleep(iDelay);
                    iFails++;
                    iDelay *= 2;
                    if (iFails > 10)
                    {
                        throw new ID3Exception("Unable to delete original file.");
                    }
                }
            }
            if (! oTmpFileSource.renameTo(m_oFileSource))
            {
                throw new ID3Exception("Unable to rename temporary file " + oTmpFileSource.toString() + " to " + m_oFileSource.toString() + ".");
            }
        }
        catch (ID3Exception e)
        {
            throw e;
        }
        catch (Exception e)
        {
            throw new ID3Exception("Error processing [" + m_oFileSource.getName() + "].", e);
        }
    
public voidremoveTags()

        removeID3V1Tag();
        
        removeID3V2Tag();
    
public voidsync()

        // before we write anything, check first that if there is a v2 tag to be sync'ed, it is a valid tag to write
        // (it is not valid unless it has at least one frame)
        if ((m_oID3V2Tag != null) && ( ! m_oID3V2Tag.containsAtLeastOneFrame()))
        {
            throw new ID3Exception("This file has an ID3 V2 tag which cannot be written because it does not contain at least one frame.");
        }

        if (m_oID3V1Tag != null)
        {
            // need to update the V1 tags
            v1Sync();
        }
        if (m_oID3V2Tag != null)
        {
            // need to update the V2 tags
            v2Sync();
        }
    
private voidv1Sync()
Update the contents of the actual MP3 file to reflect the current ID3 V1 tag settings of the object.

throws
ID3Exception if an error updating the file occurs

        IFileSource oTmpFileSource = null;
        InputStream oSourceIS = null;
        OutputStream oTmpOS = null;

        try
        {
            // open source file for reading
            try
            {
                oSourceIS = new BufferedInputStream(m_oFileSource.getInputStream());
            }
            catch (Exception e)
            {
                throw new ID3Exception("Error opening [" + m_oFileSource.getName() + "]", e);
            }

            try
            {
                // create temporary file to work with
                try
                {
                    oTmpFileSource = m_oFileSource.createTempFile("id3.", ".tmp");
                }
                catch (Exception e)
                {
                    throw new ID3Exception("Unable to create temporary file.", e);
                }

                // open temp file for writing
                try
                {
                    oTmpOS = oTmpFileSource.getOutputStream();
                }
                catch (Exception e)
                {
                    throw new ID3Exception("Error opening temporary file for writing.", e);
                }

                try
                {
                    // copy over all of the source file up to but not including the V1 tags,
                    // if they are present
                    long lFileLength = m_oFileSource.length();
                    // copy over all of the file up to the last 128 bytes (in 64k blocks for speed, while remaining memory efficient)
                    byte[] abyBuffer = new byte[65536];
                    long lCopied = 0;
                    long lTotalToCopy = lFileLength - 128;
                    while (lCopied < lTotalToCopy)
                    {
                        long lLeftToCopy = lTotalToCopy - lCopied;
                        long lToCopyNow = (lLeftToCopy >= 65536) ? 65536 : lLeftToCopy;
                        oSourceIS.read(abyBuffer, 0, (int)lToCopyNow);
                        oTmpOS.write(abyBuffer, 0, (int)lToCopyNow);
                        lCopied += lToCopyNow;
                    }


                    // check next three bytes of source file which indicate whether this file already
                    // has a V1 tag on it or not
                    byte[] abyCheckTag = new byte[3];
                    oSourceIS.read(abyCheckTag);
                    if ( ! ((abyCheckTag[0] == 'T") && (abyCheckTag[1] == 'A") && (abyCheckTag[2] == 'G")))
                    {
                        // no V1 tag on this file... copy the rest of it over (3 + 125 = 128 bytes)
                        oTmpOS.write(abyCheckTag);
                        for (int i=0; i < 125; i++)
                        {
                            oTmpOS.write(oSourceIS.read());
                        }
                    }

                    // append V1 tag information to the end of the data copied from the source file
                    // to the temporary file
                    m_oID3V1Tag.write(oTmpOS);

                    // we're done
                    oTmpOS.flush();
                }
                finally
                {
                    oTmpOS.close();
                }
            }
            finally
            {
                oSourceIS.close();
            }

            // move temp file to original source file
            if (! m_oFileSource.delete())
            {
                //HACK:  This is a hack, to get around the fact that at least some JVMs are buggy, in that files which
                //       have been closed are hung onto, pending garbage collection.  By suggesting garbage collection,
                //       the next time, the delete -magically- works.
                int iFails = 1;
                int iDelay = 1;
                while (!m_oFileSource.delete())
                {
                    System.gc();    // this will close the open file
                    Thread.sleep(iDelay);
                    iFails++;
                    iDelay *= 2;
                    if (iFails > 10)
                    {
                        throw new ID3Exception("Unable to delete original file.");
                    }
                }
            }
            if (! oTmpFileSource.renameTo(m_oFileSource))
            {
                throw new ID3Exception("Unable to rename temporary file " + oTmpFileSource.toString() + " to " + m_oFileSource.toString() + ".");
            }
        }
        catch (ID3Exception e)
        {
            throw e;
        }
        catch (Exception e)
        {
            throw new ID3Exception("Error processing [" + m_oFileSource.getName() + "].", e);
        }
    
private voidv2Sync()
Update the contents of the actual MP3 file to reflect the current ID3 V2 tag settings of the object.

throws
ID3Exception if an error updating the file occurs

        IFileSource oTmpFileSource = null;
        InputStream oSourceIS = null;
        OutputStream oTmpOS = null;
        
        // check first if this tag can be written (ie. unregistered crypto agents, etc.)
        m_oID3V2Tag.sanityCheck();
        
        try
        {
            // open source file for reading
            try
            {
                oSourceIS = new BufferedInputStream(m_oFileSource.getInputStream());
            }
            catch (Exception e)
            {
                throw new ID3Exception("Error opening [" + m_oFileSource.getName() + "]", e);
            }

            try
            {
                // create temporary file to work with
                try
                {
                    oTmpFileSource = m_oFileSource.createTempFile("id3.", ".tmp");
                }
                catch (Exception e)
                {
                    throw new ID3Exception("Unable to create temporary file.", e);
                }

                // open temp file for writing
                try
                {
                    oTmpOS = new BufferedOutputStream(oTmpFileSource.getOutputStream());
                }
                catch (Exception e)
                {
                    throw new ID3Exception("Error opening temporary file for writing.", e);
                }

                try
                {
                    // write tag to beginning of file (if we were going to write somewhere other than the beginning,
                    // we'd have to deal with that somewhere in this method)
                    m_oID3V2Tag.write(oTmpOS);

                    // copy over the MP3 data from the source file to the temp file
                    // (need to check if there is a V2 tag in the source file, and skip over them if they exist)
                    byte[] abyCheckTag = new byte[3];
                    oSourceIS.read(abyCheckTag);
                    if ((abyCheckTag[0] == 'I") && (abyCheckTag[1] == 'D") && (abyCheckTag[2] == '3"))
                    {
                        // there is a tag in this file.. skip over them
                        // read version information
                        int iVersion = oSourceIS.read();
                        int iPatch = oSourceIS.read();
                        if (iVersion > 4)
                        {
                            // close and remove temp file
                            oTmpOS.close();
                            oTmpFileSource.delete();

                            throw new ID3Exception("Will not overwrite tag of version greater than 2.4.0.");
                        }
                        // skip flags
                        oSourceIS.skip(1);
                        // get tag length
                        byte[] abyTagLength = new byte[4];
                        if (oSourceIS.read(abyTagLength) != 4)
                        {
                            throw new ID3Exception("Error reading existing ID3 tag.");
                        }
                        ID3DataInputStream oID3DIS = new ID3DataInputStream(new ByteArrayInputStream(abyTagLength));
                        long iTagLength = oID3DIS.readID3Four();
                        oID3DIS.close();
                        while (iTagLength > 0)
                        {
                            long iNumSkipped = oSourceIS.skip(iTagLength);
                            if (iNumSkipped == 0)
                            {
                                throw new ID3Exception("Error reading existing ID3 tag.");
                            }
                            iTagLength -= iNumSkipped;
                        }
                    }
                    else
                    {
                        // there is no tag in this file...
                        oTmpOS.write(abyCheckTag);
                    }

                    // copy over rest of the file
                    byte[] abyBuffer = new byte[65536];
                    int iNumRead;
                    while ((iNumRead = oSourceIS.read(abyBuffer)) != -1)
                    {
                        oTmpOS.write(abyBuffer, 0, iNumRead);
                    }

                    // we're done
                    oTmpOS.flush();
                }
                finally
                {
                    oTmpOS.close();
                }
            }
            finally
            {
                oSourceIS.close();
            }
            
            // move temp file to original source file
            if (! m_oFileSource.delete())
            {
                //HACK:  This is a hack, to get around the fact that at least some JVMs are buggy, in that files which
                //       have been closed are hung onto, pending garbage collection.  By suggesting garbage collection,
                //       the next time, the delete -magically- works.
                int iFails = 1;
                int iDelay = 1;
                while (!m_oFileSource.delete())
                {
                    System.gc();    // this will close the open file
                    Thread.sleep(iDelay);
                    iFails++;
                    iDelay *= 2;
                    if (iFails > 10)
                    {
                        throw new ID3Exception("Unable to delete original file.");
                    }
                }
            }
            if (! oTmpFileSource.renameTo(m_oFileSource))
            {
                throw new ID3Exception("Unable to rename temporary file " + oTmpFileSource.toString() + " to " + m_oFileSource.toString() + ".");
            }
        }
        catch (ID3Exception e)
        {
            throw e;
        }
        catch (Exception e)
        {
            throw new ID3Exception("Error processing [" + m_oFileSource.getName() + "].", e);
        }