1 package org.farng.mp3; 2 3 import org.farng.mp3.filename.FilenameTag; 4 import org.farng.mp3.filename.FilenameTagBuilder; 5 import org.farng.mp3.id3.AbstractID3v2; 6 import org.farng.mp3.id3.AbstractID3v2Frame; 7 import org.farng.mp3.id3.ID3v1; 8 import org.farng.mp3.id3.ID3v1_1; 9 import org.farng.mp3.id3.ID3v2_2; 10 import org.farng.mp3.id3.ID3v2_3; 11 import org.farng.mp3.id3.ID3v2_4; 12 import org.farng.mp3.lyrics3.AbstractLyrics3; 13 import org.farng.mp3.lyrics3.Lyrics3v1; 14 import org.farng.mp3.lyrics3.Lyrics3v2; 15 16 import java.io.EOFException; 17 import java.io.File; 18 import java.io.FileInputStream; 19 import java.io.FileNotFoundException; 20 import java.io.FileOutputStream; 21 import java.io.IOException; 22 import java.io.RandomAccessFile; 23 import java.util.ArrayList; 24 import java.util.HashSet; 25 import java.util.Iterator; 26 import java.util.List; 27 import java.util.Set; 28 29 /** 30 * <TABLE border=0> <TBODY> <TR> <TD class=h2>How is MP3 built?</TD></TR></TBODY></TABLE> <TABLE border=0> <TBODY> <TR 31 * vAlign=top> <TD> <P>Most people with a little knowledge in MP3 files know that the sound is divided into smaller 32 * parts and compressed with a psycoacoustic model. This smaller pieces of the audio is then put into something called 33 * 'frames', which is a little datablock with a header. I'll focus on that header in this text.</P> 34 * <p/> 35 * <P>The header is 4 bytes, 32 bits, big and begins with something called sync. This sync is, at least according to the 36 * MPEG standard, 12 set bits in a row. Some add-on standards made later uses 11 set bits and one cleared bit. The sync 37 * is directly followed by a ID bit, indicating if the file is a MPEG-1 och MPEG-2 file. 0=MPEG-2 and 1=MPEG-1</P> 38 * <p/> 39 * <P>The layer is defined with the two layers bits. They are oddly defined as</P> <CENTER> <TABLE cellSpacing=0 40 * cellPadding=2 border=1> <TBODY> <TR> <TD>0 0</TD> <TD>Not defined</TD></TR> <TR> <TD>0 1</TD> <TD>Layer III</TD></TR> 41 * <TR> <TD>1 0</TD> <TD>Layer II</TD></TR> <TR> <TD>1 1</TD> <TD>Layer I</TD></TR></TBODY></TABLE> </CENTER> 42 * <p/> 43 * <P>With this information and the information in the bitrate field we can determine the bitrate of the audio (in 44 * kbit/s) according to this table.</P> <CENTER> <TABLE cellSpacing=0 cellPadding=2 border=1> <TBODY> <TR> 45 * <TD>Bitrate<BR>value</TD> <TD>MPEG-1,<BR>layer I</TD> <TD>MPEG-1,<BR>layer II</TD> <TD>MPEG-1,<BR>layer III</TD> 46 * <TD>MPEG-2,<BR>layer I</TD> <TD>MPEG-2,<BR>layer II</TD> <TD>MPEG-2,<BR>layer III</TD></TR> <TR> <TD>0 0 0 0</TD> 47 * <TD></TD> <TD></TD> <TD></TD> <TD></TD> <TD></TD> <TD></TD></TR> <TR> <TD>0 0 0 1</TD> <TD>32</TD> <TD>32</TD> 48 * <TD>32</TD> <TD>32</TD> <TD>32</TD> <TD>8</TD></TR> <TR> <TD>0 0 1 0</TD> <TD>64</TD> <TD>48</TD> <TD>40</TD> 49 * <TD>64</TD> <TD>48</TD> <TD>16</TD></TR> <TR> <TD>0 0 1 1</TD> <TD>96</TD> <TD>56</TD> <TD>48</TD> <TD>96</TD> 50 * <TD>56</TD> <TD>24</TD></TR> <TR> <TD>0 1 0 0</TD> <TD>128</TD> <TD>64</TD> <TD>56</TD> <TD>128</TD> <TD>64</TD> 51 * <TD>32</TD></TR> <TR> <TD>0 1 0 1</TD> <TD>160</TD> <TD>80</TD> <TD>64</TD> <TD>160</TD> <TD>80</TD> <TD>64</TD></TR> 52 * <TR> <TD>0 1 1 0</TD> <TD>192</TD> <TD>96</TD> <TD>80</TD> <TD>192</TD> <TD>96</TD> <TD>80</TD></TR> <TR> <TD>0 1 1 53 * 1</TD> <TD>224</TD> <TD>112</TD> <TD>96</TD> <TD>224</TD> <TD>112</TD> <TD>56</TD></TR> <TR> <TD>1 0 0 0</TD> 54 * <TD>256</TD> <TD>128</TD> <TD>112</TD> <TD>256</TD> <TD>128</TD> <TD>64</TD></TR> <TR> <TD>1 0 0 1</TD> <TD>288</TD> 55 * <TD>160</TD> <TD>128</TD> <TD>288</TD> <TD>160</TD> <TD>128</TD></TR> <TR> <TD>1 0 1 0</TD> <TD>320</TD> <TD>192</TD> 56 * <TD>160</TD> <TD>320</TD> <TD>192</TD> <TD>160</TD></TR> <TR> <TD>1 0 1 1</TD> <TD>352</TD> <TD>224</TD> <TD>192</TD> 57 * <TD>352</TD> <TD>224</TD> <TD>112</TD></TR> <TR> <TD>1 1 0 0</TD> <TD>384</TD> <TD>256</TD> <TD>224</TD> <TD>384</TD> 58 * <TD>256</TD> <TD>128</TD></TR> <TR> <TD>1 1 0 1</TD> <TD>416</TD> <TD>320</TD> <TD>256</TD> <TD>416</TD> <TD>320</TD> 59 * <TD>256</TD></TR> <TR> <TD>1 1 1 0</TD> <TD>448</TD> <TD>384</TD> <TD>320</TD> <TD>448</TD> <TD>384</TD> 60 * <TD>320</TD></TR> <TR> <TD>1 1 1 1</TD> <TD></TD> <TD></TD> <TD></TD> <TD></TD> <TD></TD> 61 * <TD></TD></TR></TBODY></TABLE> </CENTER> 62 * <p/> 63 * <P>The sample rate is described in the frequency field. These values is dependent of which MPEG standard is used 64 * according to the following table.</P> <CENTER> <TABLE cellSpacing=0 cellPadding=2 border=1> <TBODY> <TR> 65 * <TD>Frequency<BR>value</TD> <TD>MPEG-1</TD> <TD>MPEG-2</TD></TR> <TR> <TD>0 0</TD> <TD>44100 Hz</TD> <TD>22050 66 * Hz</TD></TR> <TR> <TD>0 1</TD> <TD>48000 Hz</TD> <TD>24000 Hz</TD></TR> <TR> <TD>1 0</TD> <TD>32000 Hz</TD> <TD>16000 67 * Hz</TD></TR> <TR> <TD>1 1</TD> <TD></TD> <TD></TD></TR></TBODY></TABLE> </CENTER> 68 * <p/> 69 * <P>Three bits is not needed in the decoding process at all. These are the copyright bit, original home bit and the 70 * private bit. The copyright has the same meaning as the copyright bit on CDs and DAT tapes, i.e. telling that it is 71 * illegal to copy the contents if the bit is set. The original home bit indicates, if set, that the frame is located on 72 * its original media. No one seems to know what the privat bit is good for. 73 * <p/> 74 * <p/> 75 * <p/> 76 * <P>If the protection bit is NOT set then the frame header is followed by a 16 bit checksum, inserted before the audio 77 * data. If the padding bit is set then the frame is padded with an extra byte. Knowing this the size of the complete 78 * frame can be calculated with the following formula</P> <CENTER> <P>FrameSize = 144 * BitRate / SampleRate<BR>when the 79 * padding bit is cleared and</P> 80 * <p/> 81 * <P>FrameSize = (144 * BitRate / SampleRate) + 1<BR>when the padding bit is set. 82 * <p/> 83 * <P></CENTER> 84 * <p/> 85 * <P>The frameSize is of course an integer. If for an example BitRate=128000, SampleRate=44100 and the padding bit is 86 * cleared, then the FrameSize = 144 * 128000 / 44100 = 417 87 * <p/> 88 * <p/> 89 * <p/> 90 * <P>The mode field is used to tell which sort of stereo/mono encoding that has been used. The purpose of the mode 91 * extension field is different for different layers, but I really don't know exactly what it's for.</P> <CENTER> <TABLE 92 * cellSpacing=0 cellPadding=2 border=1> <TBODY> <TR> <TD>Mode value</TD> <TD>mode</TD></TR> <TR> <TD>0 0</TD> 93 * <TD>Stereo</TD></TR> <TR> <TD>0 1</TD> <TD>Joint stereo</TD></TR> <TR> <TD>1 0</TD> <TD>Dual channel</TD></TR> <TR> 94 * <TD>1 1</TD> <TD>Mono</TD></TR></TBODY></TABLE> </CENTER> 95 * <p/> 96 * <P>The last field is the emphasis field. It is used to sort of 're-equalize' the sound after a Dolby-like noise 97 * supression. This is not very used and will probably never be. The following noise supression model is used</P> 98 * <CENTER> <TABLE cellSpacing=0 cellPadding=2 border=1> <TBODY> <TR> <TD>Emphasis value</TD> <TD>Emphasis 99 * method</TD></TR> <TR> <TD>0 0</TD> <TD>none</TD></TR> <TR> <TD>0 1</TD> <TD>50/15ms</TD></TR> <TR> <TD>1 0</TD> 100 * <TD></TD></TR> <TR> <TD>1 1</TD> <TD>CCITT j.17</TD></TR></TBODY></TABLE> </CENTER></TD> </TR></TBODY></TABLE> 101 * 102 * @author Eric Farng 103 * @version $Revision: 1.5 $ 104 */ 105 public class MP3File { 106 107 /** 108 * the ID3v2 tag that this file contains. 109 */ 110 private AbstractID3v2 id3v2tag; 111 /** 112 * the Lyrics3 tag that this file contains. 113 */ 114 private AbstractLyrics3 lyrics3tag; 115 /** 116 * the mp3 file that this instance represents. This value can be null. This value is also used for any methods that 117 * are called without a file argument 118 */ 119 private File mp3file; 120 /** 121 * the ID3v2_4 tag that represents the parsed filename. 122 */ 123 private FilenameTag filenameTag; 124 /** 125 * the ID3v1 tag that this file contains. 126 */ 127 private ID3v1 id3v1tag; 128 /** 129 * value read from the MP3 Frame header 130 */ 131 private boolean copyProtected; 132 /** 133 * value read from the MP3 Frame header 134 */ 135 private boolean home; 136 /** 137 * value read from the MP3 Frame header 138 */ 139 private boolean padding; 140 /** 141 * value read from the MP3 Frame header 142 */ 143 private boolean privacy; 144 /** 145 * value read from the MP3 Frame header 146 */ 147 private boolean protection; 148 /** 149 * value read from the MP3 Frame header 150 */ 151 private boolean variableBitRate; 152 /** 153 * value read from the MP3 Frame header 154 */ 155 private byte emphasis; 156 /** 157 * value read from the MP3 Frame header 158 */ 159 private byte layer; 160 /** 161 * value read from the MP3 Frame header 162 */ 163 private byte mode; 164 /** 165 * value read from the MP3 Frame header 166 */ 167 private byte modeExtension; 168 /** 169 * value read from the MP3 Frame header 170 */ 171 private byte mpegVersion; 172 /** 173 * frequency determined from MP3 Version and frequency value read from the MP3 Frame header 174 */ 175 private double frequency; 176 /** 177 * bitrate calculated from the frame MP3 Frame header 178 */ 179 private int bitRate; 180 181 /** 182 * Creates a new empty MP3File object that is not associated with a specific file. 183 */ MP3File()184 public MP3File() { 185 super(); 186 } 187 188 /** 189 * Creates a new MP3File object. 190 */ MP3File(final MP3File copyObject)191 public MP3File(final MP3File copyObject) { 192 super(); 193 copyProtected = copyObject.copyProtected; 194 home = copyObject.home; 195 padding = copyObject.padding; 196 privacy = copyObject.privacy; 197 protection = copyObject.protection; 198 variableBitRate = copyObject.variableBitRate; 199 emphasis = copyObject.emphasis; 200 layer = copyObject.layer; 201 mode = copyObject.mode; 202 modeExtension = copyObject.modeExtension; 203 mpegVersion = copyObject.mpegVersion; 204 frequency = copyObject.frequency; 205 bitRate = copyObject.bitRate; 206 mp3file = new File(copyObject.mp3file.getAbsolutePath()); 207 filenameTag = new FilenameTag(copyObject.filenameTag); 208 id3v2tag = (AbstractID3v2) TagUtility.copyObject(copyObject.id3v2tag); 209 lyrics3tag = (AbstractLyrics3) TagUtility.copyObject(copyObject.lyrics3tag); 210 id3v1tag = (ID3v1) TagUtility.copyObject(copyObject.id3v1tag); 211 } 212 213 /** 214 * Creates a new MP3File object and parse the tag from the given filename. 215 * 216 * @param filename MP3 file 217 * 218 * @throws IOException on any I/O error 219 * @throws TagException on any exception generated by this library. 220 */ MP3File(final String filename)221 public MP3File(final String filename) throws IOException, TagException { 222 this(new File(filename)); 223 } 224 225 /** 226 * Creates a new MP3File object and parse the tag from the given file Object. 227 * 228 * @param file MP3 file 229 * 230 * @throws IOException on any I/O error 231 * @throws TagException on any exception generated by this library. 232 */ MP3File(final File file)233 public MP3File(final File file) throws IOException, TagException { 234 this(file, true); 235 } 236 237 /** 238 * Creates a new MP3File object and parse the tag from the given file Object. 239 * 240 * @param file MP3 file 241 * @param writeable open in read (false) or read-write (true) mode 242 * 243 * @throws IOException on any I/O error 244 * @throws TagException on any exception generated by this library. 245 */ MP3File(final File file, final boolean writeable)246 public MP3File(final File file, final boolean writeable) throws IOException, TagException { 247 super(); 248 mp3file = file; 249 final RandomAccessFile newFile = new RandomAccessFile(file, writeable ? "rw" : "r"); 250 try { 251 id3v1tag = new ID3v1_1(newFile); 252 } catch (TagNotFoundException ex) { 253 // tag might be different version 254 } 255 try { 256 if (id3v1tag == null) { 257 id3v1tag = new ID3v1(newFile); 258 } 259 } catch (TagNotFoundException ex) { 260 // ok if it's null 261 } 262 try { 263 id3v2tag = new ID3v2_4(newFile); 264 } catch (TagNotFoundException ex) { 265 // maybe different version 266 } 267 try { 268 if (id3v2tag == null) { 269 id3v2tag = new ID3v2_3(newFile); 270 } 271 } catch (TagNotFoundException ex) { 272 // maybe a different version 273 } 274 try { 275 if (id3v2tag == null) { 276 id3v2tag = new ID3v2_2(newFile); 277 } 278 } catch (TagNotFoundException ex) { 279 // it's ok to be null 280 } 281 try { 282 lyrics3tag = new Lyrics3v2(newFile); 283 } catch (TagNotFoundException ex) { 284 // maybe a different version 285 } 286 try { 287 if (lyrics3tag == null) { 288 lyrics3tag = new Lyrics3v1(newFile); 289 } 290 } catch (TagNotFoundException ex) { 291 //it's ok to be null 292 } 293 newFile.close(); 294 try { 295 filenameTag = FilenameTagBuilder.createFilenameTagFromMP3File(this); 296 } catch (Exception ex) { 297 throw new TagException("Unable to create FilenameTag", ex); 298 } 299 } 300 getBitRate()301 public int getBitRate() { 302 return bitRate; 303 } 304 isCopyProtected()305 public boolean isCopyProtected() { 306 return copyProtected; 307 } 308 getEmphasis()309 public byte getEmphasis() { 310 return emphasis; 311 } 312 313 /** 314 * Sets the filename tag for this MP3 File. Refer to <code>TagUtilities.parseFileName</code> and 315 * <code>TagUtilities.createID3v2Tag</code> for more information about parsing file names into <code>ID3v2_4</code> 316 * objects. 317 * 318 * @param filenameTag parsed <code>ID3v2_4</code> filename tag 319 */ setFilenameTag(final FilenameTag filenameTag)320 public void setFilenameTag(final FilenameTag filenameTag) { 321 this.filenameTag = filenameTag; 322 } 323 324 /** 325 * Sets the filename tag for this MP3 File. Refer to <code>TagUtilities.parseFileName</code> and 326 * <code>TagUtilities.createID3v2Tag</code> for more information about parsing file names into <code>ID3v2_4</code> 327 * objects. 328 * 329 * @return parsed <code>ID3v2_4</code> filename tag 330 */ getFilenameTag()331 public FilenameTag getFilenameTag() { 332 return filenameTag; 333 } 334 335 /** 336 * Sets all four (id3v1, lyrics3, filename, id3v2) tags in this instance to the <code>frame</code> argument if the 337 * tag exists. This method does not use the options inside the <code>tagOptions</code> object. 338 * 339 * @param frame frame to set / replace in all four tags. 340 */ 341 //todo this method is very inefficient. setFrameAcrossTags(final AbstractID3v2Frame frame)342 public void setFrameAcrossTags(final AbstractID3v2Frame frame) { 343 if (id3v1tag != null) { 344 final ID3v2_4 id3v1 = new ID3v2_4(id3v1tag); 345 id3v1.setFrame(frame); 346 id3v1tag.overwrite(id3v1); 347 } 348 if (id3v2tag != null) { 349 id3v2tag.setFrame(frame); 350 } 351 if (lyrics3tag != null) { 352 final ID3v2_4 lyrics3 = new ID3v2_4(lyrics3tag); 353 lyrics3.setFrame(frame); 354 lyrics3tag = new Lyrics3v2(lyrics3); 355 } 356 if (filenameTag != null) { 357 filenameTag.setFrame(frame); 358 } 359 } 360 361 /** 362 * Gets the frames from all four (id3v1, lyrics3, filename, id3v2) mp3 tags in this instance for each tag that 363 * exists. This method does not use the options inside the <code>tagOptions</code> object. 364 * 365 * @param identifier ID3v2.4 Tag Frame Identifier. 366 * 367 * @return ArrayList of all instances of the desired frame. Each instance is returned as an 368 * <code>ID3v2_4Frame</code>. The nature of the code returns the array in a specific order, but this order 369 * is not guaranteed for future versions of this library. 370 */ 371 //todo this method is very inefficient. getFrameAcrossTags(final String identifier)372 public List getFrameAcrossTags(final String identifier) { 373 if (identifier != null && identifier.length() > 0) { 374 final List list = new ArrayList(32); 375 Iterator iterator; 376 if (id3v1tag != null) { 377 final ID3v2_4 id3v1 = new ID3v2_4(id3v1tag); 378 if (id3v1.hasFrameOfType(identifier)) { 379 iterator = id3v1.getFrameOfType(identifier); 380 while (iterator.hasNext()) { 381 list.add(iterator.next()); 382 } 383 } 384 } 385 if (id3v2tag != null) { 386 if (id3v2tag.hasFrameOfType(identifier)) { 387 iterator = id3v2tag.getFrameOfType(identifier); 388 while (iterator.hasNext()) { 389 list.add(iterator.next()); 390 } 391 } 392 } 393 if (lyrics3tag != null) { 394 final ID3v2_4 lyrics3 = new ID3v2_4(lyrics3tag); 395 if (lyrics3.hasFrameOfType(identifier)) { 396 iterator = lyrics3.getFrameOfType(identifier); 397 while (iterator.hasNext()) { 398 list.add(iterator.next()); 399 } 400 } 401 } 402 if (filenameTag != null) { 403 if (filenameTag.hasFrameOfType(identifier)) { 404 iterator = filenameTag.getFrameOfType(identifier); 405 while (iterator.hasNext()) { 406 list.add(iterator.next()); 407 } 408 } 409 } 410 return list; 411 } 412 return null; 413 } 414 getFrequency()415 public double getFrequency() { 416 return frequency; 417 } 418 isHome()419 public boolean isHome() { 420 return home; 421 } 422 423 /** 424 * Sets the <code>ID3v1</code> tag for this object. A new <code>ID3v1_1</code> object is created from the argument 425 * and then used here. 426 * 427 * @param mp3tag Any MP3Tag object can be used and will be converted into a new ID3v1_1 object. 428 */ setID3v1Tag(final AbstractMP3Tag mp3tag)429 public void setID3v1Tag(final AbstractMP3Tag mp3tag) { 430 id3v1tag = new ID3v1_1(mp3tag); 431 } 432 setID3v1Tag(final ID3v1 id3v1tag)433 public void setID3v1Tag(final ID3v1 id3v1tag) { 434 this.id3v1tag = id3v1tag; 435 } 436 437 /** 438 * Returns the <code>ID3v1</code> tag for this object. 439 * 440 * @return the <code>ID3v1</code> tag for this object 441 */ getID3v1Tag()442 public ID3v1 getID3v1Tag() { 443 return id3v1tag; 444 } 445 446 /** 447 * Sets the <code>ID3v2</code> tag for this object. A new <code>ID3v2_4</code> object is created from the argument 448 * and then used here. 449 * 450 * @param mp3tag Any MP3Tag object can be used and will be converted into a new ID3v2_4 object. 451 */ setID3v2Tag(final AbstractMP3Tag mp3tag)452 public void setID3v2Tag(final AbstractMP3Tag mp3tag) { 453 id3v2tag = new ID3v2_4(mp3tag); 454 } 455 setID3v2Tag(final AbstractID3v2 id3v2tag)456 public void setID3v2Tag(final AbstractID3v2 id3v2tag) { 457 this.id3v2tag = id3v2tag; 458 } 459 460 /** 461 * Returns the <code>ID3v2</code> tag for this object. 462 * 463 * @return the <code>ID3v2</code> tag for this object 464 */ getID3v2Tag()465 public AbstractID3v2 getID3v2Tag() { 466 return id3v2tag; 467 } 468 getLayer()469 public byte getLayer() { 470 return layer; 471 } 472 473 /** 474 * Sets the <code>Lyrics3</code> tag for this object. A new <code>Lyrics3v2</code> object is created from the 475 * argument and then used here. 476 * 477 * @param mp3tag Any MP3Tag object can be used and will be converted into a new Lyrics3v2 object. 478 */ setLyrics3Tag(final AbstractMP3Tag mp3tag)479 public void setLyrics3Tag(final AbstractMP3Tag mp3tag) { 480 lyrics3tag = new Lyrics3v2(mp3tag); 481 } 482 setLyrics3Tag(final AbstractLyrics3 lyrics3tag)483 public void setLyrics3Tag(final AbstractLyrics3 lyrics3tag) { 484 this.lyrics3tag = lyrics3tag; 485 } 486 487 /** 488 * Returns the <code>ID3v1</code> tag for this object. 489 * 490 * @return the <code>ID3v1</code> tag for this object 491 */ getLyrics3Tag()492 public AbstractLyrics3 getLyrics3Tag() { 493 return lyrics3tag; 494 } 495 getMode()496 public byte getMode() { 497 return mode; 498 } 499 getModeExtension()500 public byte getModeExtension() { 501 return modeExtension; 502 } 503 504 /** 505 * Returns the byte position of the first MP3 Frame that this object refers to. This is the first byte of music data 506 * and not the ID3 Tag Frame. 507 * 508 * @return the byte position of the first MP3 Frame 509 * 510 * @throws IOException on any I/O error 511 * @throws FileNotFoundException if the file exists but is a directory rather than a regular file or cannot be 512 * opened for any other reason 513 */ getMp3StartByte()514 public long getMp3StartByte() throws IOException, FileNotFoundException { 515 return getMp3StartByte(mp3file); 516 } 517 518 /** 519 * Returns the byte position of the first MP3 Frame that the <code>file</code> arguement refers to. This is the 520 * first byte of music data and not the ID3 Tag Frame. 521 * 522 * @param file MP3 file to search 523 * 524 * @return the byte position of the first MP3 Frame 525 * 526 * @throws IOException on any I/O error 527 * @throws FileNotFoundException if the file exists but is a directory rather than a regular file or cannot be 528 * opened for any other reason 529 */ getMp3StartByte(final File file)530 public long getMp3StartByte(final File file) throws IOException, FileNotFoundException { 531 RandomAccessFile rfile = null; 532 long startByte = 0L; 533 try { 534 rfile = new RandomAccessFile(file, "r"); 535 seekMP3Frame(rfile); 536 startByte = rfile.getFilePointer(); 537 } finally { 538 if (rfile != null) { 539 rfile.close(); 540 } 541 } 542 return startByte; 543 } 544 setMp3file(final File mp3file)545 public void setMp3file(final File mp3file) { 546 this.mp3file = mp3file; 547 } 548 getMp3file()549 public File getMp3file() { 550 return mp3file; 551 } 552 getMpegVersion()553 public byte getMpegVersion() { 554 return mpegVersion; 555 } 556 isPadding()557 public boolean isPadding() { 558 return padding; 559 } 560 isPrivacy()561 public boolean isPrivacy() { 562 return privacy; 563 } 564 isProtection()565 public boolean isProtection() { 566 return protection; 567 } 568 569 /** 570 * Returns true if there are any unsynchronized tags in this object. A fragment is unsynchronized if it exists in 571 * two or more tags but is not equal across all of them. 572 * 573 * @return true of any fragments are unsynchronized. 574 */ 575 //todo there might be a faster way to do this, other than calling 576 //getUnsynchronizedFragments() isUnsynchronized()577 public boolean isUnsynchronized() { 578 return getUnsynchronizedFragments().size() > 0; 579 } 580 581 /** 582 * Returns a HashSet of unsynchronized fragments across all tags in this object. A fragment is unsynchronized if it 583 * exists in two or more tags but is not equal across all of them. 584 * 585 * @return a HashSet of unsynchronized fragments 586 */ getUnsynchronizedFragments()587 public Set getUnsynchronizedFragments() { 588 final ID3v2_4 total = new ID3v2_4(id3v2tag); 589 final Set set = new HashSet(32); 590 total.append(id3v1tag); 591 total.append(lyrics3tag); 592 total.append(filenameTag); 593 total.append(id3v2tag); 594 final ID3v2_4 id3v1 = new ID3v2_4(id3v1tag); 595 final ID3v2_4 lyrics3 = new ID3v2_4(lyrics3tag); 596 final ID3v2_4 filename = new ID3v2_4(filenameTag); 597 final AbstractID3v2 id3v2 = id3v2tag; 598 final Iterator iterator = total.iterator(); 599 while (iterator.hasNext()) { 600 final AbstractID3v2Frame frame = (AbstractID3v2Frame) iterator.next(); 601 final String identifier = frame.getIdentifier(); 602 if (id3v2 != null) { 603 if (id3v2.hasFrame(identifier)) { 604 if (!id3v2.getFrame(identifier).isSubsetOf(frame)) { 605 set.add(identifier); 606 } 607 } 608 } 609 if (id3v1.hasFrame(identifier)) { 610 if (!id3v1.getFrame(identifier).isSubsetOf(frame)) { 611 set.add(identifier); 612 } 613 } 614 if (lyrics3.hasFrame(identifier)) { 615 if (!lyrics3.getFrame(identifier).isSubsetOf(frame)) { 616 set.add(identifier); 617 } 618 } 619 if (filename.hasFrame(identifier)) { 620 if (!filename.getFrame(identifier).isSubsetOf(frame)) { 621 set.add(identifier); 622 } 623 } 624 } 625 return set; 626 } 627 setVariableBitRate(final boolean variableBitRate)628 public void setVariableBitRate(final boolean variableBitRate) { 629 this.variableBitRate = variableBitRate; 630 } 631 isVariableBitRate()632 public boolean isVariableBitRate() { 633 return variableBitRate; 634 } 635 636 /** 637 * Adjust the lenght of the ID3v2 padding at the beginning of the MP3 file referred to in this object. The ID3v2 638 * size will be calculated, then a new file will be created with enough size to fit the <code>ID3v2</code> tag in 639 * this object. The old file will be deleted, and the new file renamed. All parameters will be taken from the 640 * <code>tagOptions</code> object. 641 * 642 * @throws FileNotFoundException if the file exists but is a directory rather than a regular file or cannot be 643 * opened for any other reason 644 * @throws IOException on any I/O error 645 * @throws TagException on any exception generated by this library. 646 */ adjustID3v2Padding()647 public boolean adjustID3v2Padding() throws FileNotFoundException, IOException, TagException { 648 return adjustID3v2Padding(TagOptionSingleton.getInstance().getId3v2PaddingSize(), 649 TagOptionSingleton.getInstance().isId3v2PaddingWillShorten(), 650 TagOptionSingleton.getInstance().isId3v2PaddingCopyTag(), 651 mp3file); 652 } 653 654 /** 655 * Adjust the length of the ID3v2 padding at the beginning of the MP3 file this object refers to. The ID3v2 size 656 * will be calculated, then a new file will be created with enough size to fit the <code>ID3v2</code> tag. The old 657 * file will be deleted, and the new file renamed. 658 * 659 * @param paddingSize Initial padding size. This size is doubled until the ID3v2 tag will fit. 660 * @param willShorten if the newly calculated padding size is less than the padding length of the file, then news 661 * the new shorter padding size if this is true. 662 * @param copyID3v2Tag if true, write the <code>ID3v2</code> tag of this object into the file 663 * 664 * @throws FileNotFoundException if the file exists but is a directory rather than a regular file or cannot be 665 * opened for any other reason 666 * @throws IOException on any I/O error 667 * @throws TagException on any exception generated by this library. 668 */ adjustID3v2Padding(final int paddingSize, final boolean willShorten, final boolean copyID3v2Tag)669 public boolean adjustID3v2Padding(final int paddingSize, final boolean willShorten, final boolean copyID3v2Tag) 670 throws FileNotFoundException, IOException, TagException { 671 return adjustID3v2Padding(paddingSize, willShorten, copyID3v2Tag, mp3file); 672 } 673 674 /** 675 * Adjust the length of the ID3v2 padding at the beginning of the MP3 file this object refers to. The ID3v2 size 676 * will be calculated, then a new file will be created with enough size to fit the <code>ID3v2</code> tag. The old 677 * file will be deleted, and the new file renamed. 678 * 679 * @param paddingSize Initial padding size. This size is doubled until the ID3v2 tag will fit. A paddingSize of 680 * zero will create a padding length exactly equal to the tag size. 681 * @param willShorten Shorten the padding size by halves if the ID3v2 tag will fit 682 * @param copyID3v2Tag if true, write the <code>ID3v2</code> tag of this object into the file 683 * @param file The file to adjust the padding length of 684 * 685 * @throws FileNotFoundException if the file exists but is a directory rather than a regular file or cannot be 686 * opened for any other reason 687 * @throws IOException on any I/O error 688 * @throws TagException on any exception generated by this library. 689 */ adjustID3v2Padding(final int paddingSize, final boolean willShorten, final boolean copyID3v2Tag, final File file)690 public boolean adjustID3v2Padding(final int paddingSize, 691 final boolean willShorten, 692 final boolean copyID3v2Tag, 693 final File file) throws FileNotFoundException, IOException, TagException { 694 int id3v2TagSize = 0; 695 final long mp3start = getMp3StartByte(file); 696 long newPaddingSize = paddingSize; 697 FileOutputStream outStream = null; 698 FileInputStream inStream = null; 699 File backupFile = null; 700 File paddedFile = null; 701 if (newPaddingSize < 0) { 702 throw new TagException("Invalid paddingSize: " + newPaddingSize); 703 } 704 if (hasID3v2Tag()) { 705 id3v2TagSize = getID3v2Tag().getSize(); 706 } 707 if (newPaddingSize != 0) { 708 // double padding size until it's large enough 709 while (newPaddingSize < id3v2TagSize) { 710 newPaddingSize *= TagOptionSingleton.getInstance().getId3v2PaddingMultiplier(); 711 } 712 } 713 if (newPaddingSize < mp3start && !willShorten) { 714 return false; 715 } 716 if (newPaddingSize == mp3start) { 717 return false; 718 } 719 try { 720 // we first copy everything to a new file, then replace the original 721 paddedFile = File.createTempFile("temp", ".mp3", file.getParentFile()); 722 outStream = new FileOutputStream(paddedFile); 723 inStream = new FileInputStream(file); 724 byte[] buffer; 725 if (copyID3v2Tag == true) { 726 // paddingSize < mp3start && willshorten == false 727 // was already checked for outside of the try block. 728 if ((newPaddingSize < mp3start) && willShorten) { 729 // copy the current tag 730 buffer = new byte[(int) newPaddingSize]; 731 inStream.read(buffer, 0, buffer.length); 732 outStream.write(buffer, 0, buffer.length); 733 buffer = new byte[(int) (mp3start - newPaddingSize)]; 734 735 // skip the rest of the tag that didn't fit 736 inStream.read(buffer, 0, buffer.length); 737 738 // paddingSize > mp3start 739 } else { 740 // copy the current tag 741 buffer = new byte[(int) mp3start]; 742 inStream.read(buffer, 0, buffer.length); 743 outStream.write(buffer, 0, buffer.length); 744 745 // add zeros for the rest of the padding 746 if (newPaddingSize - mp3start > 0) { 747 buffer = new byte[(int) (newPaddingSize - mp3start)]; 748 outStream.write(buffer, 0, buffer.length); 749 } 750 } 751 } else { 752 buffer = new byte[(int) newPaddingSize]; 753 754 // skip the tag 755 inStream.skip(mp3start); 756 757 // write zeros for the tag 758 outStream.write(buffer, 0, buffer.length); 759 } 760 buffer = new byte[1024]; 761 int b = inStream.read(buffer, 0, buffer.length); 762 while (b == 1024) { 763 outStream.write(buffer, 0, buffer.length); 764 b = inStream.read(buffer, 0, buffer.length); 765 } 766 if (b != -1) { 767 outStream.write(buffer, 0, b); 768 } 769 backupFile = new File(file.getParentFile(), TagUtility.appendBeforeExtension(file.getName(), ".original")); 770 TagUtility.copyFile(file, backupFile); 771 if (backupFile.exists()) { 772 backupFile.setLastModified(file.lastModified()); 773 } else { 774 return false; 775 } 776 TagUtility.copyFile(paddedFile, file); 777 return true; 778 } finally { 779 if (inStream != null) { 780 inStream.getFD().sync(); 781 inStream.close(); 782 } 783 if (outStream != null) { 784 outStream.getFD().sync(); 785 outStream.close(); 786 } 787 if ((backupFile != null) && 788 (TagOptionSingleton.getInstance().isOriginalSavedAfterAdjustingID3v2Padding() == false)) { 789 backupFile.delete(); 790 } 791 if (paddedFile != null) { 792 paddedFile.delete(); 793 } 794 } 795 } 796 delete(final AbstractMP3Tag mp3tag)797 public void delete(final AbstractMP3Tag mp3tag) throws FileNotFoundException, IOException { 798 mp3tag.delete(new RandomAccessFile(mp3file, "rw")); 799 } 800 801 /** 802 * Returns true if this object contains an filename pseudo-tag 803 * 804 * @return true if this object contains an filename pseudo-tag 805 */ hasFilenameTag()806 public boolean hasFilenameTag() { 807 return (filenameTag != null); 808 } 809 810 /** 811 * Returns true if this object contains an <code>Id3v1</code> tag 812 * 813 * @return true if this object contains an <code>Id3v1</code> tag 814 */ hasID3v1Tag()815 public boolean hasID3v1Tag() { 816 return (id3v1tag != null); 817 } 818 819 /** 820 * Returns true if this object contains an <code>Id3v2</code> tag 821 * 822 * @return true if this object contains an <code>Id3v2</code> tag 823 */ hasID3v2Tag()824 public boolean hasID3v2Tag() { 825 return (id3v2tag != null); 826 } 827 828 /** 829 * Returns true if this object contains an <code>Lyrics3</code> tag 830 * 831 * @return true if this object contains an <code>Lyrics3</code> tag 832 */ hasLyrics3Tag()833 public boolean hasLyrics3Tag() { 834 return (lyrics3tag != null); 835 } 836 837 /** 838 * Saves the tags in this object to the file referred to by this object. It will be saved as 839 * TagConstants.MP3_FILE_SAVE_WRITE 840 * 841 * @throws IOException on any I/O error 842 * @throws TagException on any exception generated by this library. 843 */ save()844 public void save() throws IOException, TagException { 845 save(mp3file, TagOptionSingleton.getInstance().getDefaultSaveMode()); 846 } 847 848 /** 849 * Saves the tags in this object to the file referred to by this object. It will be saved as 850 * TagConstants.MP3_FILE_SAVE_WRITE 851 * 852 * @param saveMode write, overwrite, or append. Defined as <code>TagConstants.MP3_FILE_SAVE_WRITE 853 * TagConstants.MP3_FILE_SAVE_OVERWRITE TagConstants.MP3_FILE_SAVE_APPEND </code> 854 * 855 * @throws IOException on any I/O error 856 * @throws TagException on any exception generated by this library. 857 */ save(final int saveMode)858 public void save(final int saveMode) throws IOException, TagException { 859 save(mp3file, saveMode); 860 } 861 862 /** 863 * Saves the tags in this object to the file argument. It will be saved as TagConstants.MP3_FILE_SAVE_WRITE 864 * 865 * @param filename file to save the this object's tags to 866 * 867 * @throws IOException on any I/O error 868 * @throws TagException on any exception generated by this library. 869 */ save(final String filename)870 public void save(final String filename) throws IOException, TagException { 871 save(new File(filename), TagOptionSingleton.getInstance().getDefaultSaveMode()); 872 } 873 874 /** 875 * Saves the tags in this object to the file argument. It will be saved as TagConstants.MP3_FILE_SAVE_WRITE 876 * 877 * @param file file to save the this object's tags to 878 * 879 * @throws IOException on any I/O error 880 * @throws TagException on any exception generated by this library. 881 */ save(final File file)882 public void save(final File file) throws IOException, TagException { 883 save(file, TagOptionSingleton.getInstance().getDefaultSaveMode()); 884 } 885 886 /** 887 * Saves the tags in this object to the file argument. It will be saved as TagConstants.MP3_FILE_SAVE_WRITE 888 * 889 * @param filename file to save the this object's tags to 890 * @param saveMode write, overwrite, or append. Defined as <code>TagConstants.MP3_FILE_SAVE_WRITE 891 * TagConstants.MP3_FILE_SAVE_OVERWRITE TagConstants.MP3_FILE_SAVE_APPEND </code> 892 * 893 * @throws IOException on any I/O error 894 * @throws TagException on any exception generated by this library. 895 */ save(final String filename, final int saveMode)896 public void save(final String filename, final int saveMode) throws IOException, TagException { 897 save(new File(filename), saveMode); 898 } 899 900 /** 901 * Saves the tags in this object to the file argument. It will be saved as TagConstants.MP3_FILE_SAVE_WRITE 902 * 903 * @param file file to save the this object's tags to 904 * @param saveMode write, overwrite, or append. Defined as <code>TagConstants.MP3_FILE_SAVE_WRITE 905 * TagConstants.MP3_FILE_SAVE_OVERWRITE TagConstants.MP3_FILE_SAVE_APPEND </code> 906 * 907 * @throws IOException on any I/O error 908 * @throws TagException on any exception generated by this library. 909 */ save(final File file, final int saveMode)910 public void save(final File file, final int saveMode) throws IOException, TagException { 911 if ((saveMode < TagConstant.MP3_FILE_SAVE_FIRST) || (saveMode > TagConstant.MP3_FILE_SAVE_LAST)) { 912 throw new TagException("Invalid Save Mode"); 913 } 914 RandomAccessFile rfile = null; 915 try { 916 if (id3v2tag != null) { 917 adjustID3v2Padding(TagOptionSingleton.getInstance().getId3v2PaddingSize(), 918 TagOptionSingleton.getInstance().isId3v2PaddingWillShorten(), 919 TagOptionSingleton.getInstance().isId3v2PaddingCopyTag(), 920 file); 921 } 922 923 // we can't put these two if's together because 924 // adjustid3v2padding needs all handles on the file closed; 925 rfile = new RandomAccessFile(file, "rw"); 926 if (TagOptionSingleton.getInstance().isId3v2Save()) { 927 if (id3v2tag == null) { 928 if (saveMode == TagConstant.MP3_FILE_SAVE_OVERWRITE) { 929 (new ID3v2_4()).delete(rfile); 930 } 931 } else { 932 if (saveMode == TagConstant.MP3_FILE_SAVE_WRITE) { 933 id3v2tag.write(rfile); 934 } else if (saveMode == TagConstant.MP3_FILE_SAVE_APPEND) { 935 id3v2tag.append(rfile); 936 } else if (saveMode == TagConstant.MP3_FILE_SAVE_OVERWRITE) { 937 id3v2tag.overwrite(rfile); 938 } 939 } 940 } 941 if (TagOptionSingleton.getInstance().isLyrics3Save()) { 942 if (lyrics3tag == null) { 943 if (saveMode == TagConstant.MP3_FILE_SAVE_OVERWRITE) { 944 (new Lyrics3v2()).delete(rfile); 945 } 946 } else { 947 if (saveMode == TagConstant.MP3_FILE_SAVE_WRITE) { 948 lyrics3tag.write(rfile); 949 } else if (saveMode == TagConstant.MP3_FILE_SAVE_APPEND) { 950 lyrics3tag.append(rfile); 951 } else if (saveMode == TagConstant.MP3_FILE_SAVE_OVERWRITE) { 952 lyrics3tag.overwrite(rfile); 953 } 954 } 955 } 956 if (TagOptionSingleton.getInstance().isId3v1Save()) { 957 if (id3v1tag == null) { 958 if (saveMode == TagConstant.MP3_FILE_SAVE_OVERWRITE) { 959 (new ID3v1()).delete(rfile); 960 } 961 } else { 962 if (saveMode == TagConstant.MP3_FILE_SAVE_WRITE) { 963 id3v1tag.write(rfile); 964 } else if (saveMode == TagConstant.MP3_FILE_SAVE_APPEND) { 965 id3v1tag.append(rfile); 966 } else if (saveMode == TagConstant.MP3_FILE_SAVE_OVERWRITE) { 967 id3v1tag.overwrite(rfile); 968 } 969 } 970 } 971 if (TagOptionSingleton.getInstance().isFilenameTagSave()) { 972 if (filenameTag != null) { 973 if (saveMode == TagConstant.MP3_FILE_SAVE_WRITE) { 974 filenameTag.write(rfile); 975 } else if (saveMode == TagConstant.MP3_FILE_SAVE_APPEND) { 976 filenameTag.append(rfile); 977 } else if (saveMode == TagConstant.MP3_FILE_SAVE_OVERWRITE) { 978 filenameTag.overwrite(rfile); 979 } 980 } 981 } 982 } finally { 983 if (rfile != null) { 984 rfile.close(); 985 } 986 } 987 } 988 989 /** 990 * Returns true if the first MP3 frame can be found for the MP3 file that this object refers to. This is the first 991 * byte of music data and not the ID3 Tag Frame. 992 * 993 * @return true if the first MP3 frame can be found 994 * 995 * @throws IOException on any I/O error 996 */ seekMP3Frame()997 public boolean seekMP3Frame() throws IOException { 998 RandomAccessFile rfile = null; 999 boolean found = false; 1000 try { 1001 rfile = new RandomAccessFile(mp3file, "r"); 1002 found = seekMP3Frame(rfile); 1003 } finally { 1004 if (rfile != null) { 1005 rfile.close(); 1006 } 1007 } 1008 return found; 1009 } 1010 1011 /** 1012 * Returns true if the first MP3 frame can be found for the MP3 file argument. It tries to sync as many frame as 1013 * defined in <code>TagOptions.getNumberMP3SyncFrame</code> This is the first byte of music data and not the ID3 Tag 1014 * Frame. 1015 * 1016 * @param seekFile MP3 file to seek 1017 * 1018 * @return true if the first MP3 frame can be found 1019 * 1020 * @throws IOException on any I/O error 1021 */ seekMP3Frame(final RandomAccessFile seekFile)1022 public boolean seekMP3Frame(final RandomAccessFile seekFile) throws IOException { 1023 boolean syncFound = false; 1024 byte first; 1025 byte second; 1026 long filePointer = 1; 1027 variableBitRate = false; 1028 try { 1029 seekFile.seek(0); 1030 do { 1031 first = seekFile.readByte(); 1032 if (first == (byte) 0xFF) { 1033 filePointer = seekFile.getFilePointer(); 1034 second = (byte) (seekFile.readByte() & (byte) 0xE0); 1035 if (second == (byte) 0xE0) { 1036 seekFile.seek(filePointer - 1); 1037 1038 // seek the next frames, recursively 1039 syncFound = seekNextMP3Frame(seekFile, 1040 TagOptionSingleton.getInstance().getNumberMP3SyncFrame()); 1041 } 1042 seekFile.seek(filePointer); 1043 } 1044 } while (syncFound == false); 1045 seekFile.seek(filePointer - 1); 1046 } catch (EOFException ex) { 1047 syncFound = false; 1048 } catch (IOException ex) { 1049 throw ex; 1050 } 1051 return syncFound; 1052 } 1053 1054 /** 1055 * Returns the MP3 frame size for the file this object refers to. It assumes that <code>seekNextMP3Frame</code> has 1056 * already been called. 1057 * 1058 * @return MP3 Frame size in bytes. 1059 */ getFrameSize()1060 private int getFrameSize() { 1061 if (frequency == 0) { 1062 return 0; 1063 } 1064 final int size; 1065 final int paddingByte = padding ? 1 : 0; 1066 if (layer == 3) { // Layer I 1067 size = (int) ((((12 * bitRate) / frequency) + paddingByte) * 4); 1068 } else { 1069 size = (int) (((144 * bitRate) / frequency) + paddingByte); 1070 } 1071 return size; 1072 } 1073 1074 /** 1075 * Reads the mp3 frame header from the current posiiton in the file and sets this object's private variables to what 1076 * is found. It assumes the <code>RandomAccessFile</code> is already pointing to a valid MP3 Frame. 1077 * 1078 * @param file File to read frame header 1079 * 1080 * @throws IOException on any I/O error 1081 * @throws TagNotFoundException if MP3 Frame sync bites were not immediately found 1082 * @throws InvalidTagException if any of the header values are invlaid 1083 */ readFrameHeader(final RandomAccessFile file)1084 private void readFrameHeader(final RandomAccessFile file) 1085 throws IOException, TagNotFoundException, InvalidTagException { 1086 final byte[] buffer = new byte[4]; 1087 file.read(buffer); 1088 1089 // sync 1090 if ((buffer[0] != (byte) 0xFF) || ((buffer[1] & (byte) 0xE0) != (byte) 0xE0)) { 1091 throw new TagNotFoundException("MP3 Frame sync bits not found"); 1092 } 1093 mpegVersion = (byte) ((buffer[1] & TagConstant.MASK_MP3_VERSION) >> 3); 1094 layer = (byte) ((buffer[1] & TagConstant.MASK_MP3_LAYER) >> 1); 1095 protection = (buffer[1] & TagConstant.MASK_MP3_PROTECTION) != 1; 1096 final int bitRateValue = (buffer[2] & TagConstant.MASK_MP3_BITRATE) | 1097 (buffer[1] & TagConstant.MASK_MP3_ID) | 1098 (buffer[1] & TagConstant.MASK_MP3_LAYER); 1099 final Long object = (Long) TagConstant.bitrate.get(new Long(bitRateValue)); 1100 if (object != null) { 1101 if (object.longValue() != bitRate) { 1102 variableBitRate = true; 1103 } 1104 bitRate = object.intValue(); 1105 } else { 1106 throw new InvalidTagException("Invalid bit rate"); 1107 } 1108 final int frequencyValue = (buffer[2] & TagConstant.MASK_MP3_FREQUENCY) >>> 2; 1109 if (mpegVersion == 3) { // Version 1.0 1110 switch (frequencyValue) { 1111 case 0: 1112 frequency = 44.1; 1113 break; 1114 case 1: 1115 frequency = 48.0; 1116 break; 1117 case 2: 1118 frequency = 32.0; 1119 break; 1120 } 1121 } else if (mpegVersion == 2) { // Version 2.0 1122 switch (frequencyValue) { 1123 case 0: 1124 frequency = 22.05; 1125 break; 1126 case 1: 1127 frequency = 24.00; 1128 break; 1129 case 2: 1130 frequency = 16.00; 1131 break; 1132 } 1133 } else if (mpegVersion == 00) { // Version 2.5 1134 switch (frequencyValue) { 1135 case 0: 1136 frequency = 11.025; 1137 break; 1138 case 1: 1139 frequency = 12.00; 1140 break; 1141 case 2: 1142 frequency = 8.00; 1143 break; 1144 } 1145 } else { 1146 throw new InvalidTagException("Invalid MPEG version"); 1147 } 1148 padding = (buffer[2] & TagConstant.MASK_MP3_PADDING) != 0; 1149 privacy = (buffer[2] & TagConstant.MASK_MP3_PRIVACY) != 0; 1150 mode = (byte) ((buffer[3] & TagConstant.MASK_MP3_MODE) >> 6); 1151 modeExtension = (byte) ((buffer[3] & TagConstant.MASK_MP3_MODE_EXTENSION) >> 4); 1152 copyProtected = (buffer[3] & TagConstant.MASK_MP3_COPY) != 0; 1153 home = (buffer[3] & TagConstant.MASK_MP3_HOME) != 0; 1154 emphasis = (byte) ((buffer[3] & TagConstant.MASK_MP3_EMPHASIS)); 1155 } 1156 1157 /** 1158 * Returns true if the first MP3 frame can be found for the MP3 file argument. It is recursive and called by 1159 * seekMP3Frame. This is the first byte of music data and not the ID3 Tag Frame. 1160 * 1161 * @param file MP3 file to seek 1162 * @param iterations recursive counter 1163 * 1164 * @return true if the first MP3 frame can be found 1165 * 1166 * @throws IOException on any I/O error 1167 */ seekNextMP3Frame(final RandomAccessFile file, final int iterations)1168 private boolean seekNextMP3Frame(final RandomAccessFile file, final int iterations) throws IOException { 1169 final boolean syncFound; 1170 final byte[] buffer; 1171 final byte first; 1172 final byte second; 1173 final long filePointer; 1174 if (iterations == 0) { 1175 syncFound = true; 1176 } else { 1177 try { 1178 readFrameHeader(file); 1179 } catch (TagException ex) { 1180 return false; 1181 } 1182 final int size = getFrameSize(); 1183 if ((size <= 0) || (size > file.length())) { 1184 return false; 1185 } 1186 buffer = new byte[size - 4]; 1187 file.read(buffer); 1188 filePointer = file.getFilePointer(); 1189 first = file.readByte(); 1190 if (first == (byte) 0xFF) { 1191 second = (byte) (file.readByte() & (byte) 0xE0); 1192 if (second == (byte) 0xE0) { 1193 file.seek(filePointer); 1194 1195 // recursively find the next frames 1196 syncFound = seekNextMP3Frame(file, iterations - 1); 1197 } else { 1198 syncFound = false; 1199 } 1200 } else { 1201 syncFound = false; 1202 } 1203 } 1204 return syncFound; 1205 } 1206 }