Coverage Report - org.jaudiotagger.audio.mp3.MP3AudioHeader
 
Classes in this File Line Coverage Branch Coverage Complexity
MP3AudioHeader
79%
159/199
81%
75/92
2.784
 
 1  
 /**
 2  
  *  @author : Paul Taylor
 3  
  *
 4  
  *  Version @version:$Id: MP3AudioHeader.java 836 2009-11-12 15:44:07Z paultaylor $
 5  
  *
 6  
  *  MusicTag Copyright (C)2003,2004
 7  
  *
 8  
  *  This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser
 9  
  *  General Public  License as published by the Free Software Foundation; either version 2.1 of the License,
 10  
  *  or (at your option) any later version.
 11  
  *
 12  
  *  This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
 13  
  *  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 14  
  *  See the GNU Lesser General Public License for more details.
 15  
  *
 16  
  *  You should have received a copy of the GNU Lesser General Public License along with this library; if not,
 17  
  *  you can get a copy from http://www.opensource.org/licenses/lgpl-license.php or write to the Free Software
 18  
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 19  
  */
 20  
 package org.jaudiotagger.audio.mp3;
 21  
 
 22  
 import org.jaudiotagger.audio.AudioHeader;
 23  
 import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
 24  
 import org.jaudiotagger.logging.Hex;
 25  
 
 26  
 import java.io.EOFException;
 27  
 import java.io.File;
 28  
 import java.io.FileInputStream;
 29  
 import java.io.IOException;
 30  
 import java.nio.ByteBuffer;
 31  
 import java.nio.channels.FileChannel;
 32  
 import java.text.ParseException;
 33  
 import java.text.SimpleDateFormat;
 34  
 import java.util.Date;
 35  
 import java.util.Locale;
 36  
 import java.util.logging.Level;
 37  
 import java.util.logging.Logger;
 38  
 
 39  
 /**
 40  
  * Represents the audio header of an MP3 File
 41  
  * <p/>
 42  
  * <p>The audio header consists of a number of
 43  
  * audio frames. Because we are not trying to play the audio but only extract some information
 44  
  * regarding the audio we only need to read the first  audio frames to ensure that we have correctly
 45  
  * identified them as audio frames and extracted the metadata we reuire.
 46  
  * <p/>
 47  
  * <p>Start of Audio id 0xFF (11111111) and then second byte anded with 0xE0(11100000).
 48  
  * For example 2nd byte doesnt have to be 0xE0 is just has to have the top 3 signicant
 49  
  * bits set. For example 0xFB (11111011) is a common occurence of the second match. The 2nd byte
 50  
  * defines flags to indicate various mp3 values.
 51  
  * <p/>
 52  
  * <p>Having found these two values we then read the header which comprises these two bytes plus a further
 53  
  * two to ensure this really is a MP3Header, sometimes the first frame is actually a dummy frame with summary information
 54  
  * held within about the whole file, typically using a Xing Header or LAme Header. This is most useful when the file
 55  
  * is variable bit rate, if the file is variable bit rate but does not use a summary header it will not be correctly
 56  
  * identified as a VBR frame and the track length will be incorrectly calculated. Strictly speaking MP3 means
 57  
  * Layer III file but MP2 Layer II), MP1 Layer I) and MPEG-2 files are sometimes used and named with
 58  
  * the .mp3 suffix so this library attempts to supports all these formats.
 59  
  */
 60  
 public class MP3AudioHeader implements AudioHeader
 61  
 {
 62  
     protected MPEGFrameHeader mp3FrameHeader;
 63  
     protected XingFrame mp3XingFrame;
 64  
     protected VbriFrame mp3VbriFrame;
 65  
 
 66  
 
 67  
     private long fileSize;
 68  
     private long startByte;
 69  
     private double timePerFrame;
 70  
     private double trackLength;
 71  
     private long numberOfFrames;
 72  
     private long numberOfFramesEstimate;
 73  
     private long bitrate;
 74  2105
     private String encoder = "";
 75  
 
 76  4
     private static final SimpleDateFormat timeInFormat = new SimpleDateFormat("ss", Locale.UK);
 77  4
     private static final SimpleDateFormat timeOutFormat = new SimpleDateFormat("mm:ss",Locale.UK);
 78  4
     private static final SimpleDateFormat timeOutOverAnHourFormat = new SimpleDateFormat("kk:mm:ss",Locale.UK);
 79  
     private static final char isVbrIdentifier = '~';
 80  
     private static final int CONVERT_TO_KILOBITS = 1000;
 81  
     private static final String TYPE_MP3 = "mp3";
 82  
     private static final int CONVERTS_BYTE_TO_BITS = 8;
 83  
 
 84  
     //Logger
 85  4
     public static Logger logger = Logger.getLogger("org.jaudiotagger.audio.mp3");
 86  
 
 87  
     /**
 88  
      * After testing the average location of the first MP3Header bit was at 5000 bytes so this is
 89  
      * why chosen as a default.
 90  
      */
 91  
     private final static int FILE_BUFFER_SIZE = 5000;
 92  
     private final static int MIN_BUFFER_REMAINING_REQUIRED = MPEGFrameHeader.HEADER_SIZE + XingFrame.MAX_BUFFER_SIZE_NEEDED_TO_READ_XING;
 93  
     private static final int NO_SECONDS_IN_HOUR = 3600;
 94  
 
 95  
     public MP3AudioHeader()
 96  0
     {
 97  0
     }
 98  
 
 99  
     /**
 100  
      * Search for the first MP3Header in the file
 101  
      * <p/>
 102  
      * The search starts from the start of the file, it is usually safer to use the alternative constructor that
 103  
      * allows you to provide the length of the tag header as a parameter so the tag can be skipped over.
 104  
      *
 105  
      * @param seekFile
 106  
      * @throws IOException
 107  
      * @throws InvalidAudioFrameException
 108  
      */
 109  
     public MP3AudioHeader(final File seekFile) throws IOException, InvalidAudioFrameException
 110  0
     {
 111  0
         if (!seek(seekFile, 0))
 112  
         {
 113  0
             throw new InvalidAudioFrameException("No audio header found within" + seekFile.getName());
 114  
         }
 115  0
     }
 116  
 
 117  
     /**
 118  
      * Search for the first MP3Header in the file
 119  
      * <p/>
 120  
      * Starts searching from location startByte, this is because there is likely to be an ID3TagHeader
 121  
      * before the start of the audio. If this tagHeader contains unsynchronized information there is a
 122  
      * possibility that it might be inaccurately identified as the start of the Audio data. Various checks
 123  
      * are done in this code to prevent this happening but it cannot be guaranteed.
 124  
      * <p/>
 125  
      * Of course if the startByte provided overstates the length of the tag header, this could mean the
 126  
      * start of the MP3AudioHeader is missed, further checks are done within the MP3 class to recognize
 127  
      * if this has occurred and take appropriate action.
 128  
      *
 129  
      * @param seekFile
 130  
      * @param startByte
 131  
      * @throws IOException
 132  
      * @throws InvalidAudioFrameException
 133  
      */
 134  
     public MP3AudioHeader(final File seekFile, long startByte) throws IOException, InvalidAudioFrameException
 135  2105
     {
 136  2105
         if (!seek(seekFile, startByte))
 137  
         {
 138  12
             throw new InvalidAudioFrameException("No audio header found within" + seekFile.getName());
 139  
         }
 140  2093
     }
 141  
 
 142  
     /**
 143  
      * Returns true if the first MP3 frame can be found for the MP3 file
 144  
      * <p/>
 145  
      * This is the first byte of  music data and not the ID3 Tag Frame.     *
 146  
      *
 147  
      * @param seekFile  MP3 file to seek
 148  
      * @param startByte if there is an ID3v2tag we dont want to start reading from the start of the tag
 149  
      * @return true if the first MP3 frame can be found
 150  
      * @throws IOException on any I/O error
 151  
      * @noinspection NestedTryStatement
 152  
      */
 153  
     public boolean seek(final File seekFile, long startByte) throws IOException
 154  
     {
 155  
 
 156  
         //This is substantially faster than updating the filechannels position
 157  
         long filePointerCount;
 158  
 
 159  2105
         final FileInputStream fis = new FileInputStream(seekFile);
 160  2105
         final FileChannel fc = fis.getChannel();
 161  
 
 162  
         //Read into Byte Buffer in Chunks
 163  2105
         ByteBuffer bb = ByteBuffer.allocateDirect(FILE_BUFFER_SIZE);
 164  
 
 165  
         //Move FileChannel to the starting position (skipping over tag if any)
 166  2105
         fc.position(startByte);
 167  
 
 168  
         //Update filePointerCount
 169  2105
         filePointerCount = startByte;
 170  
 
 171  
         //Read from here into the byte buffer , doesn't move location of filepointer
 172  2105
         fc.read(bb, startByte);
 173  2105
         bb.flip();
 174  
 
 175  2105
         boolean syncFound = false;
 176  
         try
 177  
         {
 178  
             do
 179  
             {
 180  
                 //TODO remaining() is quite an expensive operation, isn't there a way we can work this out without
 181  
                 //interrogating the bytebuffer. Also this is rarely going to be true, and could be made less true
 182  
                 //by increasing FILE_BUFFER_SIZE
 183  309717
                 if (bb.remaining() <= MIN_BUFFER_REMAINING_REQUIRED)
 184  
                 {
 185  50
                     bb.clear();
 186  50
                     fc.position(filePointerCount);
 187  50
                     fc.read(bb, fc.position());
 188  50
                     bb.flip();
 189  50
                     if (bb.limit() <= MIN_BUFFER_REMAINING_REQUIRED)
 190  
                     {
 191  
                         //No mp3 exists
 192  12
                         return false;
 193  
                     }
 194  
                 }
 195  
                 //MP3File.logger.finest("fc:"+fc.position() + "bb"+bb.position());
 196  309705
                 if (MPEGFrameHeader.isMPEGFrame(bb))
 197  
                 {
 198  
                     try
 199  
                     {
 200  2415
                         if (MP3AudioHeader.logger.isLoggable(Level.FINEST))
 201  
                         {
 202  1180
                             MP3AudioHeader.logger.finest("Found Possible header at:" + filePointerCount);
 203  
                         }
 204  
 
 205  2415
                         mp3FrameHeader = MPEGFrameHeader.parseMPEGHeader(bb);
 206  2202
                         syncFound = true;
 207  
                         //if(2==1) use this line when you want to test getting the next frame without using xing
 208  2202
                         if (XingFrame.isXingFrame(bb, mp3FrameHeader))
 209  
                         {
 210  2023
                             if (MP3AudioHeader.logger.isLoggable(Level.FINEST))
 211  
                             {
 212  1008
                                 MP3AudioHeader.logger.finest("Found Possible XingHeader");
 213  
                             }
 214  
                             try
 215  
                             {
 216  
                                 //Parses Xing frame without modifying position of main buffer
 217  2023
                                 mp3XingFrame = XingFrame.parseXingFrame();
 218  
                             }
 219  0
                             catch (InvalidAudioFrameException ex)
 220  
                             {
 221  
                                 // We Ignore because even if Xing Header is corrupted
 222  
                                 //doesn't mean file is corrupted
 223  2023
                             }
 224  2023
                             break;
 225  
                         }
 226  179
                         else if (VbriFrame.isVbriFrame(bb, mp3FrameHeader))
 227  
                         {
 228  4
                             if (MP3AudioHeader.logger.isLoggable(Level.FINEST))
 229  
                             {
 230  0
                                 MP3AudioHeader.logger.finest("Found Possible VbriHeader");
 231  
                             }
 232  
                             try
 233  
                             {
 234  
                                 //Parses Vbri frame without modifying position of main buffer
 235  4
                                 mp3VbriFrame = VbriFrame.parseVBRIFrame();
 236  
                             }
 237  0
                             catch (InvalidAudioFrameException ex)
 238  
                             {
 239  
                                 // We Ignore because even if Vbri Header is corrupted
 240  
                                 //doesn't mean file is corrupted
 241  4
                             }
 242  4
                             break;
 243  
                         }
 244  
                         // There is a small but real chance that an unsynchronised ID3 Frame could fool the MPEG
 245  
                         // Parser into thinking it was an MPEG Header. If this happens the chances of the next bytes
 246  
                         // forming a Xing frame header are very remote. On the basis that  most files these days have
 247  
                         // Xing headers we do an additional check for when an apparent frame header has been found
 248  
                         // but is not followed by a Xing Header:We check the next header this wont impose a large
 249  
                         // overhead because wont apply to most Mpegs anyway ( Most likely to occur if audio
 250  
                         // has an  APIC frame which should have been unsynchronised but has not been) , or if the frame
 251  
                         // has been encoded with as Unicode LE because these have a BOM of 0xFF 0xFE
 252  
                         else
 253  
                         {
 254  175
                             syncFound = isNextFrameValid(seekFile, filePointerCount, bb, fc);
 255  175
                             if (syncFound)
 256  
                             {
 257  66
                                 break;
 258  
                             }
 259  
                         }
 260  
 
 261  
                     }
 262  213
                     catch (InvalidAudioFrameException ex)
 263  
                     {
 264  
                         // We Ignore because likely to be incorrect sync bits ,
 265  
                         // will just continue in loop
 266  109
                     }
 267  
                 }
 268  
 
 269  
                 //TODO position() is quite an expensive operation, isn't there a way we can work this out without
 270  
                 //interrogating the bytebuffer
 271  307612
                 bb.position(bb.position() + 1);
 272  307612
                 filePointerCount++;
 273  
 
 274  
 
 275  
             }
 276  307612
             while (!syncFound);
 277  2093
         }
 278  0
         catch (EOFException ex)
 279  
         {
 280  0
             MP3AudioHeader.logger.log(Level.WARNING, "Reached end of file without finding sync match", ex);
 281  0
             syncFound = false;
 282  0
         }
 283  0
         catch (IOException iox)
 284  
         {
 285  0
             MP3AudioHeader.logger.log(Level.SEVERE, "IOException occurred whilst trying to find sync", iox);
 286  0
             syncFound = false;
 287  0
             throw iox;
 288  
         }
 289  
         finally
 290  
         {
 291  0
             if (fc != null)
 292  
             {
 293  2105
                 fc.close();
 294  
             }
 295  
 
 296  2105
             if (fis != null)
 297  
             {
 298  2105
                 fis.close();
 299  
             }
 300  2093
         }
 301  
 
 302  
         //Return to start of audio header
 303  2093
         if (MP3AudioHeader.logger.isLoggable(Level.FINEST))
 304  
         {
 305  1034
             MP3AudioHeader.logger.finer("Return found matching mp3 header starting at" + filePointerCount);
 306  
         }
 307  2093
         setFileSize(seekFile.length());
 308  2093
         setMp3StartByte(filePointerCount);
 309  2093
         setTimePerFrame();
 310  2093
         setNumberOfFrames();
 311  2093
         setTrackLength();
 312  2093
         setBitRate();
 313  2093
         setEncoder();
 314  
         /*if((filePointerCount - startByte )>0)
 315  
         {
 316  
             logger.severe(seekFile.getName()+"length:"+startByte+"Difference:"+(filePointerCount - startByte));
 317  
         }
 318  
         */
 319  2093
         return syncFound;
 320  
     }
 321  
 
 322  
     /**
 323  
      * Called in some circumstances to check the next frame to ensure we have the correct audio header
 324  
      *
 325  
      * @param seekFile
 326  
      * @param filePointerCount
 327  
      * @param bb
 328  
      * @param fc
 329  
      * @return true if frame is valid
 330  
      * @throws java.io.IOException
 331  
      */
 332  
     private boolean isNextFrameValid(File seekFile, long filePointerCount, ByteBuffer bb, FileChannel fc) throws IOException
 333  
     {
 334  175
         if (MP3AudioHeader.logger.isLoggable(Level.FINEST))
 335  
         {
 336  70
             MP3AudioHeader.logger.finer("Checking next frame" + seekFile.getName() + ":fpc:" + filePointerCount + "skipping to:" + (filePointerCount + mp3FrameHeader.getFrameLength()));
 337  
         }
 338  175
         boolean result = false;
 339  
 
 340  175
         int currentPosition = bb.position();
 341  
 
 342  
         //Our buffer is not large enough to fit in the whole of this frame, something must
 343  
         //have gone wrong because frames are not this large, so just return false
 344  
         //bad frame header
 345  175
         if (mp3FrameHeader.getFrameLength() > (FILE_BUFFER_SIZE - MIN_BUFFER_REMAINING_REQUIRED))
 346  
         {
 347  0
             MP3AudioHeader.logger.finer("Frame size is too large to be a frame:" + mp3FrameHeader.getFrameLength());
 348  0
             return false;
 349  
         }
 350  
 
 351  
         //Check for end of buffer if not enough room get some more
 352  175
         if (bb.remaining() <= MIN_BUFFER_REMAINING_REQUIRED + mp3FrameHeader.getFrameLength())
 353  
         {
 354  8
             MP3AudioHeader.logger.finer("Buffer too small, need to reload, buffer size:" + bb.remaining());
 355  8
             bb.clear();
 356  8
             fc.position(filePointerCount);
 357  8
             fc.read(bb, fc.position());
 358  8
             bb.flip();
 359  
             //So now original buffer has been replaced, so set current position to start of buffer
 360  8
             currentPosition = 0;
 361  
             //Not enough left
 362  8
             if (bb.limit() <= MIN_BUFFER_REMAINING_REQUIRED)
 363  
             {
 364  
                 //No mp3 exists
 365  0
                 MP3AudioHeader.logger.finer("Nearly at end of file, no header found:");
 366  0
                 return false;
 367  
             }
 368  
 
 369  
             //Still Not enough left for next alleged frame size so giving up
 370  8
             if (bb.limit() <= MIN_BUFFER_REMAINING_REQUIRED + mp3FrameHeader.getFrameLength())
 371  
             {
 372  
                 //No mp3 exists
 373  8
                 MP3AudioHeader.logger.finer("Nearly at end of file, no room for next frame, no header found:");
 374  8
                 return false;
 375  
             }
 376  
         }
 377  
 
 378  
         //Position bb to the start of the alleged next frame
 379  167
         bb.position(bb.position() + mp3FrameHeader.getFrameLength());
 380  167
         if (MPEGFrameHeader.isMPEGFrame(bb))
 381  
         {
 382  
             try
 383  
             {
 384  66
                 MPEGFrameHeader.parseMPEGHeader(bb);
 385  66
                 MP3AudioHeader.logger.finer("Check next frame confirms is an audio header ");
 386  66
                 result = true;
 387  
             }
 388  0
             catch (InvalidAudioFrameException ex)
 389  
             {
 390  0
                 MP3AudioHeader.logger.finer("Check next frame has identified this is not an audio header");
 391  0
                 result = false;
 392  66
             }
 393  
         }
 394  
         else
 395  
         {
 396  101
             MP3AudioHeader.logger.finer("isMPEGFrame has identified this is not an audio header");
 397  
         }
 398  
         //Set back to the start of the previous frame
 399  167
         bb.position(currentPosition);
 400  167
         return result;
 401  
     }
 402  
 
 403  
     /**
 404  
      * Set the location of where the Audio file begins in the file
 405  
      *
 406  
      * @param startByte
 407  
      */
 408  
     protected void setMp3StartByte(final long startByte)
 409  
     {
 410  2093
         this.startByte = startByte;
 411  2093
     }
 412  
 
 413  
 
 414  
     /**
 415  
      * Returns the byte position of the first MP3 Frame that the
 416  
      * <code>file</code> arguement refers to. This is the first byte of music
 417  
      * data and not the ID3 Tag Frame.
 418  
      *
 419  
      * @return the byte position of the first MP3 Frame
 420  
      */
 421  
     public long getMp3StartByte()
 422  
     {
 423  5532
         return startByte;
 424  
     }
 425  
 
 426  
 
 427  
     /**
 428  
      * Set number of frames in this file, use Xing if exists otherwise ((File Size - Non Audio Part)/Frame Size)
 429  
      */
 430  
     protected void setNumberOfFrames()
 431  
     {
 432  2093
         numberOfFramesEstimate = (fileSize - startByte) / mp3FrameHeader.getFrameLength();
 433  
 
 434  2093
         if (mp3XingFrame != null && mp3XingFrame.isFrameCountEnabled())
 435  
         {
 436  2023
             numberOfFrames = mp3XingFrame.getFrameCount();
 437  
         }
 438  70
         else if (mp3VbriFrame != null)
 439  
         {
 440  4
             numberOfFrames = mp3VbriFrame.getFrameCount();
 441  
         }
 442  
         else
 443  
         {
 444  66
             numberOfFrames = numberOfFramesEstimate;
 445  
         }
 446  
 
 447  2093
     }
 448  
 
 449  
     /**
 450  
      * @return The number of frames within the Audio File, calculated as accurrately as possible
 451  
      */
 452  
     public long getNumberOfFrames()
 453  
     {
 454  39
         return numberOfFrames;
 455  
     }
 456  
 
 457  
     /**
 458  
      * @return The number of frames within the Audio File, calculated by dividing the filesize by
 459  
      *         the number of frames, this may not be the most accurate method available.
 460  
      */
 461  
     public long getNumberOfFramesEstimate()
 462  
     {
 463  0
         return numberOfFramesEstimate;
 464  
     }
 465  
 
 466  
     /**
 467  
      * Set the time each frame contributes to the audio in fractions of seconds, the higher
 468  
      * the sampling rate the shorter the audio segment provided by the frame,
 469  
      * the number of samples is fixed by the MPEG Version and Layer
 470  
      */
 471  
     protected void setTimePerFrame()
 472  
     {
 473  2093
         timePerFrame = mp3FrameHeader.getNoOfSamples() / mp3FrameHeader.getSamplingRate().doubleValue();
 474  
 
 475  
         //Because when calculating framelength we may have altered the calculation slightly for MPEGVersion2
 476  
         //to account for mono/stero we seem to have to make a corresponding modification to get the correct time
 477  2093
         if ((mp3FrameHeader.getVersion() == MPEGFrameHeader.VERSION_2) || (mp3FrameHeader.getVersion() == MPEGFrameHeader.VERSION_2_5))
 478  
         {
 479  64
             if ((mp3FrameHeader.getLayer() == MPEGFrameHeader.LAYER_II) || (mp3FrameHeader.getLayer() == MPEGFrameHeader.LAYER_III))
 480  
             {
 481  64
                 if (mp3FrameHeader.getNumberOfChannels() == 1)
 482  
                 {
 483  52
                     timePerFrame = timePerFrame / 2;
 484  
                 }
 485  
             }
 486  
         }
 487  2093
     }
 488  
 
 489  
     /**
 490  
      * @return the the time each frame contributes to the audio in fractions of seconds
 491  
      */
 492  
     private double getTimePerFrame()
 493  
     {
 494  2093
         return timePerFrame;
 495  
     }
 496  
 
 497  
     /**
 498  
      * Estimate the length of the audio track in seconds
 499  
      * Calculation is Number of frames multiplied by the Time Per Frame using the first frame as a prototype
 500  
      * Time Per Frame is the number of samples in the frame (which is defined by the MPEGVersion/Layer combination)
 501  
      * divided by the sampling rate, i.e the higher the sampling rate the shorter the audio represented by the frame is going
 502  
      * to be.
 503  
      */
 504  
     protected void setTrackLength()
 505  
     {
 506  2093
         trackLength = numberOfFrames * getTimePerFrame();
 507  2093
     }
 508  
 
 509  
 
 510  
     /**
 511  
      * @return Track Length in seconds
 512  
      */
 513  
     public double getPreciseTrackLength()
 514  
     {
 515  135
         return trackLength;
 516  
     }
 517  
 
 518  
     public int getTrackLength()
 519  
     {
 520  135
         return (int) getPreciseTrackLength();
 521  
     }
 522  
 
 523  
     /**
 524  
      * Return the length in user friendly format
 525  
      * @return
 526  
      */
 527  
     public String getTrackLengthAsString()
 528  
     {
 529  
         final Date timeIn;
 530  
         try
 531  
         {
 532  135
             final long lengthInSecs = getTrackLength();
 533  135
             synchronized(timeInFormat)
 534  
             {
 535  135
                 timeIn = timeInFormat.parse(String.valueOf(lengthInSecs));
 536  135
             }
 537  
 
 538  135
             if (lengthInSecs < NO_SECONDS_IN_HOUR)
 539  
             {
 540  135
                 synchronized(timeOutFormat)
 541  
                 {
 542  135
                     return timeOutFormat.format(timeIn);
 543  0
                 }
 544  
             }
 545  
             else
 546  
             {
 547  0
                 synchronized(timeOutOverAnHourFormat)
 548  
                 {
 549  0
                     return timeOutOverAnHourFormat.format(timeIn);
 550  0
                 }
 551  
             }
 552  
         }
 553  0
         catch (ParseException pe)
 554  
         {
 555  0
             logger.warning("Unable to parse:"+getPreciseTrackLength() +" failed with ParseException:"+pe.getMessage());
 556  0
             return "";
 557  
         }
 558  
     }
 559  
 
 560  
     /**
 561  
      * @return the audio file type
 562  
      */
 563  
     public String getEncodingType()
 564  
     {
 565  52
         return TYPE_MP3;
 566  
     }
 567  
 
 568  
     /**
 569  
      * Set bitrate in kbps, if Vbr use Xingheader if possible
 570  
      */
 571  
     protected void setBitRate()
 572  
     {
 573  
 
 574  2093
         if (mp3XingFrame != null && mp3XingFrame.isVbr())
 575  
         {
 576  33
             if (mp3XingFrame.isAudioSizeEnabled() && mp3XingFrame.getAudioSize() > 0)
 577  
             {
 578  33
                 bitrate = (long) ((mp3XingFrame.getAudioSize() * CONVERTS_BYTE_TO_BITS) / (timePerFrame * getNumberOfFrames() * CONVERT_TO_KILOBITS));
 579  
             }
 580  
             else
 581  
             {
 582  0
                 bitrate = (long) (((fileSize - startByte) * CONVERTS_BYTE_TO_BITS) / (timePerFrame * getNumberOfFrames() * CONVERT_TO_KILOBITS));
 583  
             }
 584  
         }
 585  2060
         else if (mp3VbriFrame != null)
 586  
         {
 587  4
             if (mp3VbriFrame.getAudioSize() > 0)
 588  
             {
 589  4
                 bitrate = (long) ((mp3VbriFrame.getAudioSize() * CONVERTS_BYTE_TO_BITS) / (timePerFrame * getNumberOfFrames() * CONVERT_TO_KILOBITS));
 590  
             }
 591  
             else
 592  
             {
 593  0
                 bitrate = (long) (((fileSize - startByte) * CONVERTS_BYTE_TO_BITS) / (timePerFrame * getNumberOfFrames() * CONVERT_TO_KILOBITS));
 594  
             }
 595  
         }
 596  
         else
 597  
         {
 598  2056
             bitrate = mp3FrameHeader.getBitRate();
 599  
         }
 600  2093
     }
 601  
 
 602  
     protected void setEncoder()
 603  
     {
 604  2093
         if (mp3XingFrame != null)
 605  
         {
 606  2023
             if (mp3XingFrame.getLameFrame() != null)
 607  
             {
 608  2021
                 encoder = mp3XingFrame.getLameFrame().getEncoder();
 609  
             }
 610  
         }
 611  70
         else if (mp3VbriFrame != null)
 612  
         {
 613  4
             encoder = mp3VbriFrame.getEncoder();
 614  
         }
 615  2093
     }
 616  
 
 617  
     /**
 618  
      * @return bitrate in kbps, no indicator is provided as to whether or not it is vbr
 619  
      */
 620  
     public long getBitRateAsNumber()
 621  
     {
 622  2
         return bitrate;
 623  
     }
 624  
 
 625  
     /**
 626  
      * @return the BitRate of the Audio, to distinguish cbr from vbr we add a '~'
 627  
      *         for vbr.
 628  
      */
 629  
     public String getBitRate()
 630  
     {
 631  52
         if (mp3XingFrame != null && mp3XingFrame.isVbr())
 632  
         {
 633  24
             return isVbrIdentifier + String.valueOf(bitrate);
 634  
         }
 635  28
         else if (mp3VbriFrame != null)
 636  
         {
 637  0
             return isVbrIdentifier + String.valueOf(bitrate);
 638  
         }
 639  
         else
 640  
         {
 641  28
             return String.valueOf(bitrate);
 642  
         }
 643  
     }
 644  
 
 645  
 
 646  
     /**
 647  
      * @return the sampling rate in Hz
 648  
      */
 649  
     public int getSampleRateAsNumber()
 650  
     {
 651  0
         return mp3FrameHeader.getSamplingRate();
 652  
     }
 653  
 
 654  
     /**
 655  
      * @return the sampling rate as string
 656  
      */
 657  
     public String getSampleRate()
 658  
     {
 659  52
         return String.valueOf(mp3FrameHeader.getSamplingRate());
 660  
     }
 661  
 
 662  
     /**
 663  
      * @return MPEG Version (1-3)
 664  
      */
 665  
     public String getMpegVersion()
 666  
     {
 667  55
         return mp3FrameHeader.getVersionAsString();
 668  
     }
 669  
 
 670  
     /**
 671  
      * @return MPEG Layer (1-3)
 672  
      */
 673  
     public String getMpegLayer()
 674  
     {
 675  55
         return mp3FrameHeader.getLayerAsString();
 676  
     }
 677  
 
 678  
     /**
 679  
      * @return the format of the audio (i.e. MPEG-1 Layer3)
 680  
      */
 681  
     public String getFormat()
 682  
     {
 683  0
         return mp3FrameHeader.getVersionAsString() + " " + mp3FrameHeader.getLayerAsString();
 684  
     }
 685  
 
 686  
     /**
 687  
      * @return the Channel Mode such as Stero or Mono
 688  
      */
 689  
     public String getChannels()
 690  
     {
 691  55
         return mp3FrameHeader.getChannelModeAsString();
 692  
     }
 693  
 
 694  
     /**
 695  
      * @return Emphasis
 696  
      */
 697  
     public String getEmphasis()
 698  
     {
 699  0
         return mp3FrameHeader.getEmphasisAsString();
 700  
     }
 701  
 
 702  
     /**
 703  
      * @return if the bitrate is variable, Xing header takes precedence if we have one
 704  
      */
 705  
     public boolean isVariableBitRate()
 706  
     {
 707  54
         if (mp3XingFrame != null)
 708  
         {
 709  36
             return mp3XingFrame.isVbr();
 710  
         }
 711  18
         else if (mp3VbriFrame != null)
 712  
         {
 713  2
             return mp3VbriFrame.isVbr();
 714  
         }
 715  
         else
 716  
         {
 717  16
             return mp3FrameHeader.isVariableBitRate();
 718  
         }
 719  
     }
 720  
 
 721  
     public boolean isProtected()
 722  
     {
 723  52
         return mp3FrameHeader.isProtected();
 724  
     }
 725  
 
 726  
     public boolean isPrivate()
 727  
     {
 728  52
         return mp3FrameHeader.isPrivate();
 729  
     }
 730  
 
 731  
     public boolean isCopyrighted()
 732  
     {
 733  52
         return mp3FrameHeader.isCopyrighted();
 734  
     }
 735  
 
 736  
     public boolean isOriginal()
 737  
     {
 738  52
         return mp3FrameHeader.isOriginal();
 739  
     }
 740  
 
 741  
     public boolean isPadding()
 742  
     {
 743  0
         return mp3FrameHeader.isPadding();
 744  
     }
 745  
 
 746  
     /**
 747  
      * @return encoder
 748  
      */
 749  
     public String getEncoder()
 750  
     {
 751  54
         return encoder;
 752  
     }
 753  
 
 754  
     /**
 755  
      * Set the size of the file, required in some calculations
 756  
      *
 757  
      * @param fileSize
 758  
      */
 759  
     protected void setFileSize(long fileSize)
 760  
     {
 761  2093
         this.fileSize = fileSize;
 762  2093
     }
 763  
 
 764  
 
 765  
     /**
 766  
      * @return a string represntation
 767  
      */
 768  
     public String toString()
 769  
     {
 770  95
         String s = "fileSize:" + fileSize + " encoder:" + encoder + " startByte:" + Hex.asHex(startByte) + " numberOfFrames:" + numberOfFrames + " numberOfFramesEst:" + numberOfFramesEstimate + " timePerFrame:" + timePerFrame + " bitrate:" + bitrate + " trackLength:" + getTrackLengthAsString();
 771  
 
 772  95
         if (this.mp3FrameHeader != null)
 773  
         {
 774  95
             s += mp3FrameHeader.toString();
 775  
         }
 776  
 
 777  95
         if (this.mp3XingFrame != null)
 778  
         {
 779  80
             s += mp3XingFrame.toString();
 780  
         }
 781  95
         return s;
 782  
     }
 783  
 }