Coverage Report - org.jaudiotagger.audio.mp3.MP3AudioHeader
 
Classes in this File Line Coverage Branch Coverage Complexity
MP3AudioHeader
80%
161/200
77%
71/92
2.649
 
 1  
 /**
 2  
  *  @author : Paul Taylor
 3  
  *
 4  
  *  Version @version:$Id: MP3AudioHeader.java,v 1.34 2008/10/30 15:14:53 paultaylor Exp $
 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  453
     private String encoder = "";
 75  
 
 76  48
     private static final SimpleDateFormat timeInFormat = new SimpleDateFormat("ss", Locale.UK);
 77  48
     private static final SimpleDateFormat timeOutFormat = new SimpleDateFormat("mm:ss",Locale.UK);
 78  48
     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  48
     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) == false)
 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  453
     {
 136  453
         if (seek(seekFile, startByte) == false)
 137  
         {
 138  3
             throw new InvalidAudioFrameException("No audio header found within" + seekFile.getName());
 139  
         }
 140  450
     }
 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  
         //This is substantially faster than updating the filechannels position
 156  
         long filePointerCount;
 157  
 
 158  453
         final FileInputStream fis = new FileInputStream(seekFile);
 159  453
         final FileChannel fc = fis.getChannel();
 160  
 
 161  
         //Read into Byte Buffer in Chunks
 162  453
         ByteBuffer bb = ByteBuffer.allocateDirect(FILE_BUFFER_SIZE);
 163  
 
 164  
         //Move FileChannel to the starting position (skipping over tag if any)
 165  453
         fc.position(startByte);
 166  
 
 167  
         //Update filePointerCount
 168  453
         filePointerCount = startByte;
 169  
 
 170  
         //Read from here into the byte buffer , doesnt move location of filepointer
 171  453
         fc.read(bb, startByte);
 172  453
         bb.flip();
 173  
 
 174  453
         boolean syncFound = false;
 175  
         try
 176  
         {
 177  
             do
 178  
             {
 179  99415
                 if (bb.remaining() <= MIN_BUFFER_REMAINING_REQUIRED)
 180  
                 {
 181  17
                     bb.clear();
 182  17
                     fc.position(filePointerCount);
 183  17
                     fc.read(bb, fc.position());
 184  17
                     bb.flip();
 185  17
                     if (bb.limit() <= MIN_BUFFER_REMAINING_REQUIRED)
 186  
                     {
 187  
                         //No mp3 exists
 188  3
                         return false;
 189  
                     }
 190  
                 }
 191  
                 //MP3File.logger.finest("fc:"+fc.position() + "bb"+bb.position());
 192  99412
                 if (MPEGFrameHeader.isMPEGFrame(bb))
 193  
                 {
 194  
                     try
 195  
                     {
 196  553
                         if (MP3AudioHeader.logger.isLoggable(Level.FINEST))
 197  
                         {
 198  553
                             MP3AudioHeader.logger.finest("Found Possible header at:" + filePointerCount);
 199  
                         }
 200  
 
 201  553
                         mp3FrameHeader = MPEGFrameHeader.parseMPEGHeader(bb);
 202  493
                         syncFound = true;
 203  
                         //if(2==1) use this line when you want to test getting the next frame without using xing
 204  493
                         if (XingFrame.isXingFrame(bb, mp3FrameHeader))
 205  
                         {
 206  425
                             if (MP3AudioHeader.logger.isLoggable(Level.FINEST))
 207  
                             {
 208  425
                                 MP3AudioHeader.logger.finest("Found Possible XingHeader");
 209  
                             }
 210  
                             try
 211  
                             {
 212  
                                 //Parses Xing frame without modifying position of main buffer
 213  425
                                 mp3XingFrame = XingFrame.parseXingFrame();
 214  
                             }
 215  0
                             catch (InvalidAudioFrameException ex)
 216  
                             {
 217  
                                 // We Ignore because even if Xing Header is corrupted
 218  
                                 //doesn't mean file is corrupted
 219  425
                             }
 220  425
                             break;
 221  
                         }
 222  68
                         else if (VbriFrame.isVbriFrame(bb, mp3FrameHeader))
 223  
                         {
 224  4
                             if (MP3AudioHeader.logger.isLoggable(Level.FINEST))
 225  
                             {
 226  4
                                 MP3AudioHeader.logger.finest("Found Possible VbriHeader");
 227  
                             }
 228  
                             try
 229  
                             {
 230  
                                 //Parses Vbri frame without modifying position of main buffer
 231  4
                                 mp3VbriFrame = VbriFrame.parseVBRIFrame();
 232  
                             }
 233  0
                             catch (InvalidAudioFrameException ex)
 234  
                             {
 235  
                                 // We Ignore because even if Vbri Header is corrupted
 236  
                                 //doesn't mean file is corrupted
 237  4
                             }
 238  4
                             break;
 239  
                         }
 240  
                         // There is a small but real chance that an unsynchronised ID3 Frame could fool the MPEG
 241  
                         // Parser into thinking it was an MPEG Header. If this happens the chances of the next bytes
 242  
                         // forming a Xing frame header are very remote. On the basis that  most files these days have
 243  
                         // Xing headers we do an additional check for when an apparent frame header has been found
 244  
                         // but is not followed by a Xing Header:We check the next header this wont impose a large
 245  
                         // overhead because wont apply to most Mpegs anyway ( Most likely to occur if audio
 246  
                         // has an  APIC frame which should have been unsynchronised but has not been) , or if the frame
 247  
                         // has been encoded with as Unicode LE because these have a BOM of 0xFF 0xFE
 248  
                         else
 249  
                         {
 250  64
                             syncFound = isNextFrameValid(seekFile, filePointerCount, bb, fc);
 251  64
                             if (syncFound == true)
 252  
                             {
 253  21
                                 break;
 254  
                             }
 255  
                         }
 256  
 
 257  
                     }
 258  60
                     catch (InvalidAudioFrameException ex)
 259  
                     {
 260  
                         // We Ignore because likely to be incorrect sync bits ,
 261  
                         // will just continue in loop
 262  43
                     }
 263  
                 }
 264  98962
                 bb.position(bb.position() + 1);
 265  98962
                 filePointerCount++;
 266  
             }
 267  98962
             while (!syncFound);
 268  450
         }
 269  0
         catch (EOFException ex)
 270  
         {
 271  0
             MP3AudioHeader.logger.log(Level.WARNING, "Reached end of file without finding sync match", ex);
 272  0
             syncFound = false;
 273  0
         }
 274  0
         catch (IOException iox)
 275  
         {
 276  0
             MP3AudioHeader.logger.log(Level.SEVERE, "IOException occurred whilst trying to find sync", iox);
 277  0
             syncFound = false;
 278  0
             throw iox;
 279  
         }
 280  
         finally
 281  
         {
 282  0
             if (fc != null)
 283  
             {
 284  453
                 fc.close();
 285  
             }
 286  
 
 287  453
             if (fis != null)
 288  
             {
 289  453
                 fis.close();
 290  
             }
 291  450
         }
 292  
 
 293  
         //Return to start of audio header
 294  450
         if (MP3AudioHeader.logger.isLoggable(Level.FINEST))
 295  
         {
 296  450
             MP3AudioHeader.logger.finer("Return found matching mp3 header starting at" + filePointerCount);
 297  
         }
 298  450
         setFileSize(seekFile.length());
 299  450
         setMp3StartByte(filePointerCount);
 300  450
         setTimePerFrame();
 301  450
         setNumberOfFrames();
 302  450
         setTrackLength();
 303  450
         setBitRate();
 304  450
         setEncoder();
 305  450
         return syncFound;
 306  
     }
 307  
 
 308  
     /**
 309  
      * Called in some circumstances to check the next frame to ensure we have the correct audio header
 310  
      *
 311  
      * @return true if frame is valid
 312  
      */
 313  
     private boolean isNextFrameValid(File seekFile, long filePointerCount, ByteBuffer bb, FileChannel fc) throws IOException
 314  
     {
 315  64
         if (MP3AudioHeader.logger.isLoggable(Level.FINEST))
 316  
         {
 317  64
             MP3AudioHeader.logger.finer("Checking next frame" + seekFile.getName() + ":fpc:" + filePointerCount + "skipping to:" + (filePointerCount + mp3FrameHeader.getFrameLength()));
 318  
         }
 319  64
         boolean result = false;
 320  
 
 321  64
         int currentPosition = bb.position();
 322  
 
 323  
         //Our buffer is not large enough to fit in the whole of this frame, something must
 324  
         //have gone wrong because frames are not this large, so just return false
 325  
         //bad frame header
 326  64
         if (mp3FrameHeader.getFrameLength() > (FILE_BUFFER_SIZE - MIN_BUFFER_REMAINING_REQUIRED))
 327  
         {
 328  0
             MP3AudioHeader.logger.finer("Frame size is too large to be a frame:" + mp3FrameHeader.getFrameLength());
 329  0
             return false;
 330  
         }
 331  
 
 332  
         //Check for end of buffer if not enough room get some more
 333  64
         if (bb.remaining() <= MIN_BUFFER_REMAINING_REQUIRED + mp3FrameHeader.getFrameLength())
 334  
         {
 335  2
             MP3AudioHeader.logger.finer("Buffer too small, need to reload, buffer size:" + bb.remaining());
 336  2
             bb.clear();
 337  2
             fc.position(filePointerCount);
 338  2
             fc.read(bb, fc.position());
 339  2
             bb.flip();
 340  
             //So now original buffer has been replaced, so set current position to start of buffer
 341  2
             currentPosition = 0;
 342  
             //Not enough left
 343  2
             if (bb.limit() <= MIN_BUFFER_REMAINING_REQUIRED)
 344  
             {
 345  
                 //No mp3 exists
 346  0
                 MP3AudioHeader.logger.finer("Nearly at end of file, no header found:");
 347  0
                 return false;
 348  
             }
 349  
 
 350  
             //Still Not enough left for next alleged frame size so giving up
 351  2
             if (bb.limit() <= MIN_BUFFER_REMAINING_REQUIRED + mp3FrameHeader.getFrameLength())
 352  
             {
 353  
                 //No mp3 exists
 354  2
                 MP3AudioHeader.logger.finer("Nearly at end of file, no room for next frame, no header found:");
 355  2
                 return false;
 356  
             }
 357  
         }
 358  
 
 359  
         //Position bb to the start of the alleged next frame
 360  62
         bb.position(bb.position() + mp3FrameHeader.getFrameLength());
 361  62
         if (MPEGFrameHeader.isMPEGFrame(bb))
 362  
         {
 363  
             try
 364  
             {
 365  21
                 MPEGFrameHeader.parseMPEGHeader(bb);
 366  21
                 MP3AudioHeader.logger.finer("Check next frame confirms is an audio header ");
 367  21
                 result = true;
 368  
             }
 369  0
             catch (InvalidAudioFrameException ex)
 370  
             {
 371  0
                 MP3AudioHeader.logger.finer("Check next frame has identified this is not an audio header");
 372  0
                 result = false;
 373  21
             }
 374  
         }
 375  
         else
 376  
         {
 377  41
             MP3AudioHeader.logger.finer("isMPEGFrame has identified this is not an audio header");
 378  
         }
 379  
         //Set back to the start of the previous frame
 380  62
         bb.position(currentPosition);
 381  62
         return result;
 382  
     }
 383  
 
 384  
     /**
 385  
      * Set the location of where the Audio file begins in the file
 386  
      *
 387  
      * @param startByte
 388  
      */
 389  
     protected void setMp3StartByte(final long startByte)
 390  
     {
 391  450
         this.startByte = startByte;
 392  450
     }
 393  
 
 394  
 
 395  
     /**
 396  
      * Returns the byte position of the first MP3 Frame that the
 397  
      * <code>file</code> arguement refers to. This is the first byte of music
 398  
      * data and not the ID3 Tag Frame.
 399  
      *
 400  
      * @return the byte position of the first MP3 Frame
 401  
      */
 402  
     public long getMp3StartByte()
 403  
     {
 404  1189
         return startByte;
 405  
     }
 406  
 
 407  
 
 408  
     /**
 409  
      * Set number of frames in this file, use Xing if exists otherwise ((File Size - Non Audio Part)/Frame Size)
 410  
      */
 411  
     protected void setNumberOfFrames()
 412  
     {
 413  450
         numberOfFramesEstimate = (fileSize - startByte) / mp3FrameHeader.getFrameLength();
 414  
 
 415  450
         if (mp3XingFrame != null && mp3XingFrame.isFrameCountEnabled())
 416  
         {
 417  425
             numberOfFrames = mp3XingFrame.getFrameCount();
 418  
         }
 419  25
         else if (mp3VbriFrame != null)
 420  
         {
 421  4
             numberOfFrames = mp3VbriFrame.getFrameCount();
 422  
         }
 423  
         else
 424  
         {
 425  21
             numberOfFrames = numberOfFramesEstimate;
 426  
         }
 427  
 
 428  450
     }
 429  
 
 430  
     /**
 431  
      * @return The number of frames within the Audio File, calculated as accurrately as possible
 432  
      */
 433  
     public long getNumberOfFrames()
 434  
     {
 435  16
         return numberOfFrames;
 436  
     }
 437  
 
 438  
     /**
 439  
      * @return The number of frames within the Audio File, calculated by dividing the filesize by
 440  
      *         the number of frames, this may not be the most accurate method available.
 441  
      */
 442  
     public long getNumberOfFramesEstimate()
 443  
     {
 444  0
         return numberOfFramesEstimate;
 445  
     }
 446  
 
 447  
     /**
 448  
      * Set the time each frame contributes to the audio in fractions of seconds, the higher
 449  
      * the sampling rate the shorter the audio segment provided by the frame,
 450  
      * the number of samples is fixed by the MPEG Version and Layer
 451  
      */
 452  
     protected void setTimePerFrame()
 453  
     {
 454  450
         timePerFrame = mp3FrameHeader.getNoOfSamples() / mp3FrameHeader.getSamplingRate().doubleValue();
 455  
 
 456  
         //Because when calculating framelength we may have altered the calculation slightly for MPEGVersion2
 457  
         //to account for mono/stero we seem to have to make a corresponding modification to get the correct time
 458  450
         if ((mp3FrameHeader.getVersion() == MPEGFrameHeader.VERSION_2) || (mp3FrameHeader.getVersion() == MPEGFrameHeader.VERSION_2_5))
 459  
         {
 460  16
             if ((mp3FrameHeader.getLayer() == MPEGFrameHeader.LAYER_II) || (mp3FrameHeader.getLayer() == MPEGFrameHeader.LAYER_III))
 461  
             {
 462  16
                 if (mp3FrameHeader.getNumberOfChannels() == 1)
 463  
                 {
 464  13
                     timePerFrame = timePerFrame / 2;
 465  
                 }
 466  
             }
 467  
         }
 468  450
     }
 469  
 
 470  
     /**
 471  
      * @return the the time each frame contributes to the audio in fractions of seconds
 472  
      */
 473  
     private double getTimePerFrame()
 474  
     {
 475  450
         return timePerFrame;
 476  
     }
 477  
 
 478  
     /**
 479  
      * Estimate the length of the audio track in seconds
 480  
      * Calculation is Number of frames multiplied by the Time Per Frame using the first frame as a prototype
 481  
      * Time Per Frame is the number of samples in the frame (which is defined by the MPEGVersion/Layer combination)
 482  
      * divided by the sampling rate, i.e the higher the sampling rate the shorter the audio represented by the frame is going
 483  
      * to be.
 484  
      */
 485  
     protected void setTrackLength()
 486  
     {
 487  450
         trackLength = numberOfFrames * getTimePerFrame();
 488  450
     }
 489  
 
 490  
 
 491  
     /**
 492  
      * @return Track Length in seconds
 493  
      */
 494  
     public double getPreciseTrackLength()
 495  
     {
 496  1035
         return trackLength;
 497  
     }
 498  
 
 499  
     public int getTrackLength()
 500  
     {
 501  1035
         return (int) getPreciseTrackLength();
 502  
     }
 503  
 
 504  
     /**
 505  
      * Return the length in user friendly format
 506  
      */
 507  
     public String getTrackLengthAsString()
 508  
     {
 509  
         final Date timeIn;
 510  
         try
 511  
         {
 512  1035
             final long lengthInSecs = getTrackLength();
 513  1035
             synchronized(timeInFormat)
 514  
             {
 515  1035
                 timeIn = timeInFormat.parse(String.valueOf(lengthInSecs));
 516  1035
             }
 517  
 
 518  1035
             if (lengthInSecs < NO_SECONDS_IN_HOUR)
 519  
             {
 520  1035
                 synchronized(timeOutFormat)
 521  
                 {
 522  1035
                     return timeOutFormat.format(timeIn);
 523  0
                 }
 524  
             }
 525  
             else
 526  
             {
 527  0
                 synchronized(timeOutOverAnHourFormat)
 528  
                 {
 529  0
                     return timeOutOverAnHourFormat.format(timeIn);
 530  0
                 }
 531  
             }
 532  
         }
 533  0
         catch (ParseException pe)
 534  
         {
 535  0
             logger.warning("Unable to parse:"+getPreciseTrackLength() +" failed with ParseException:"+pe.getMessage());
 536  0
             return "";
 537  
         }
 538  
     }
 539  
 
 540  
     /**
 541  
      * @return the audio file type
 542  
      */
 543  
     public String getEncodingType()
 544  
     {
 545  13
         return TYPE_MP3;
 546  
     }
 547  
 
 548  
     /**
 549  
      * Set bitrate in kbps, if Vbr use Xingheader if possible
 550  
      */
 551  
     protected void setBitRate()
 552  
     {
 553  
 
 554  450
         if (mp3XingFrame != null && mp3XingFrame.isVbr())
 555  
         {
 556  10
             if (mp3XingFrame.isAudioSizeEnabled() && mp3XingFrame.getAudioSize() > 0)
 557  
             {
 558  10
                 bitrate = (long) ((mp3XingFrame.getAudioSize() * CONVERTS_BYTE_TO_BITS) / (timePerFrame * getNumberOfFrames() * CONVERT_TO_KILOBITS));
 559  
             }
 560  
             else
 561  
             {
 562  0
                 bitrate = (long) (((fileSize - startByte) * CONVERTS_BYTE_TO_BITS) / (timePerFrame * getNumberOfFrames() * CONVERT_TO_KILOBITS));
 563  
             }
 564  
         }
 565  440
         else if (mp3VbriFrame != null)
 566  
         {
 567  4
             if (mp3VbriFrame.getAudioSize() > 0)
 568  
             {
 569  4
                 bitrate = (long) ((mp3VbriFrame.getAudioSize() * CONVERTS_BYTE_TO_BITS) / (timePerFrame * getNumberOfFrames() * CONVERT_TO_KILOBITS));
 570  
             }
 571  
             else
 572  
             {
 573  0
                 bitrate = (long) (((fileSize - startByte) * CONVERTS_BYTE_TO_BITS) / (timePerFrame * getNumberOfFrames() * CONVERT_TO_KILOBITS));
 574  
             }
 575  
         }
 576  
         else
 577  
         {
 578  436
             bitrate = mp3FrameHeader.getBitRate();
 579  
         }
 580  450
     }
 581  
 
 582  
     protected void setEncoder()
 583  
     {
 584  450
         if (mp3XingFrame != null)
 585  
         {
 586  425
             if (mp3XingFrame.getLameFrame() != null)
 587  
             {
 588  423
                 encoder = mp3XingFrame.getLameFrame().getEncoder();
 589  423
                 return;
 590  
             }
 591  
         }
 592  25
         else if (mp3VbriFrame != null)
 593  
         {
 594  4
             encoder = mp3VbriFrame.getEncoder();
 595  
         }
 596  27
     }
 597  
 
 598  
     /**
 599  
      * @return bitrate in kbps, no indicator is provided as to whether or not it is vbr
 600  
      */
 601  
     public long getBitRateAsNumber()
 602  
     {
 603  2
         return bitrate;
 604  
     }
 605  
 
 606  
     /**
 607  
      * @return the BitRate of the Audio, to distinguish cbr from vbr we add a '~'
 608  
      *         for vbr.
 609  
      */
 610  
     public String getBitRate()
 611  
     {
 612  13
         if (mp3XingFrame != null && mp3XingFrame.isVbr())
 613  
         {
 614  6
             return isVbrIdentifier + String.valueOf(bitrate);
 615  
         }
 616  7
         else if (mp3VbriFrame != null)
 617  
         {
 618  0
             return isVbrIdentifier + String.valueOf(bitrate);
 619  
         }
 620  
         else
 621  
         {
 622  7
             return String.valueOf(bitrate);
 623  
         }
 624  
     }
 625  
 
 626  
 
 627  
     /**
 628  
      * @return the sampling rate in Hz
 629  
      */
 630  
     public int getSampleRateAsNumber()
 631  
     {
 632  0
         return mp3FrameHeader.getSamplingRate();
 633  
     }
 634  
 
 635  
     /**
 636  
      * @return the sampling rate as string
 637  
      */
 638  
     public String getSampleRate()
 639  
     {
 640  13
         return String.valueOf(mp3FrameHeader.getSamplingRate());
 641  
     }
 642  
 
 643  
     /**
 644  
      * @return MPEG Version (1-3)
 645  
      */
 646  
     public String getMpegVersion()
 647  
     {
 648  16
         return mp3FrameHeader.getVersionAsString();
 649  
     }
 650  
 
 651  
     /**
 652  
      * @return MPEG Layer (1-3)
 653  
      */
 654  
     public String getMpegLayer()
 655  
     {
 656  16
         return mp3FrameHeader.getLayerAsString();
 657  
     }
 658  
 
 659  
     /**
 660  
      * @return the format of the audio (i.e. MPEG-1 Layer3)
 661  
      */
 662  
     public String getFormat()
 663  
     {
 664  0
         return mp3FrameHeader.getVersionAsString() + " " + mp3FrameHeader.getLayerAsString();
 665  
     }
 666  
 
 667  
     /**
 668  
      * @return the Channel Mode such as Stero or Mono
 669  
      */
 670  
     public String getChannels()
 671  
     {
 672  16
         return mp3FrameHeader.getChannelModeAsString();
 673  
     }
 674  
 
 675  
     /**
 676  
      * @return Emphasis
 677  
      */
 678  
     public String getEmphasis()
 679  
     {
 680  0
         return mp3FrameHeader.getEmphasisAsString();
 681  
     }
 682  
 
 683  
     /**
 684  
      * @return if the bitrate is variable, Xing header takes precedence if we have one
 685  
      */
 686  
     public boolean isVariableBitRate()
 687  
     {
 688  15
         if (mp3XingFrame != null)
 689  
         {
 690  9
             return mp3XingFrame.isVbr();
 691  
         }
 692  6
         else if (mp3VbriFrame != null)
 693  
         {
 694  2
             return mp3VbriFrame.isVbr();
 695  
         }
 696  
         else
 697  
         {
 698  4
             return mp3FrameHeader.isVariableBitRate();
 699  
         }
 700  
     }
 701  
 
 702  
     public boolean isProtected()
 703  
     {
 704  13
         return mp3FrameHeader.isProtected();
 705  
     }
 706  
 
 707  
     public boolean isPrivate()
 708  
     {
 709  13
         return mp3FrameHeader.isPrivate();
 710  
     }
 711  
 
 712  
     public boolean isCopyrighted()
 713  
     {
 714  13
         return mp3FrameHeader.isCopyrighted();
 715  
     }
 716  
 
 717  
     public boolean isOriginal()
 718  
     {
 719  13
         return mp3FrameHeader.isOriginal();
 720  
     }
 721  
 
 722  
     public boolean isPadding()
 723  
     {
 724  0
         return mp3FrameHeader.isPadding();
 725  
     }
 726  
 
 727  
     /**
 728  
      * @return encoder
 729  
      */
 730  
     public String getEncoder()
 731  
     {
 732  15
         return encoder;
 733  
     }
 734  
 
 735  
     /**
 736  
      * Set the size of the file, required in some calculations
 737  
      *
 738  
      * @param fileSize
 739  
      */
 740  
     protected void setFileSize(long fileSize)
 741  
     {
 742  450
         this.fileSize = fileSize;
 743  450
     }
 744  
 
 745  
 
 746  
     /**
 747  
      * @return a string represntation
 748  
      */
 749  
     public String toString()
 750  
     {
 751  25
         String s = "fileSize:" + fileSize + " encoder:" + encoder + " startByte:" + Hex.asHex(startByte) + " numberOfFrames:" + numberOfFrames + " numberOfFramesEst:" + numberOfFramesEstimate + " timePerFrame:" + timePerFrame + " bitrate:" + bitrate + " trackLength:" + getTrackLengthAsString();
 752  
 
 753  25
         if (this.mp3FrameHeader != null)
 754  
         {
 755  25
             s += mp3FrameHeader.toString();
 756  
         }
 757  
 
 758  25
         if (this.mp3XingFrame != null)
 759  
         {
 760  20
             s += mp3XingFrame.toString();
 761  
         }
 762  25
         return s;
 763  
     }
 764  
 }