| 1 | package org.farng.mp3.id3; |
| 2 | |
| 3 | import org.farng.mp3.AbstractMP3Tag; |
| 4 | import org.farng.mp3.InvalidTagException; |
| 5 | import org.farng.mp3.MP3File; |
| 6 | import org.farng.mp3.TagConstant; |
| 7 | import org.farng.mp3.TagException; |
| 8 | import org.farng.mp3.TagNotFoundException; |
| 9 | |
| 10 | import java.io.IOException; |
| 11 | import java.io.RandomAccessFile; |
| 12 | import java.util.Iterator; |
| 13 | |
| 14 | /** |
| 15 | * <p class=t> The two biggest design goals were to be able to implement ID3v2 without disturbing old software too much |
| 16 | * and that ID3v2 should be as flexible and expandable as possible. </p> |
| 17 | * <p/> |
| 18 | * <p class=t> The first criterion is met by the simple fact that the <a href="#MPEG">MPEG</a> decoding software uses a |
| 19 | * syncsignal, embedded in the audiostream, to 'lock on to' the audio. Since the ID3v2 tag doesn't contain a valid |
| 20 | * syncsignal, no software will attempt to play the tag. If, for any reason, coincidence make a syncsignal appear within |
| 21 | * the tag it will be taken care of by the 'unsynchronisation scheme' described in <a href="#sec5">section 5</a>. </p> |
| 22 | * <p/> |
| 23 | * <p class=t> The second criterion has made a more noticeable impact on the design of the ID3v2 tag. It is constructed |
| 24 | * as a container for several information blocks, called frames, whose format need not be known to the software that |
| 25 | * encounters them. At the start of every frame there is an identifier that explains the frames' format and content, and |
| 26 | * a size descriptor that allows software to skip unknown frames. </p> |
| 27 | * <p/> |
| 28 | * <p class=t> If a total revision of the ID3v2 tag should be needed, there is a version number and a size descriptor in |
| 29 | * the ID3v2 header. </p> |
| 30 | * <p/> |
| 31 | * <p class=t> The ID3 tag described in this document is mainly targeted at files encoded with <a |
| 32 | * href="#MPEG">MPEG</a>-1/2 layer I, <a href="#MPEG">MPEG</a>-1/2 layer II, <a href="#MPEG">MPEG</a>-1/2 layer III and |
| 33 | * MPEG-2.5, but may work with other types of encoded audio. </p> |
| 34 | * <p/> |
| 35 | * <p class=t> The bitorder in ID3v2 is most significant bit first (MSB). The byteorder in multibyte numbers is most |
| 36 | * significant byte first (e.g. $12345678 would be encoded $12 34 56 78). </p> |
| 37 | * <p/> |
| 38 | * <p class=t> It is permitted to include padding after all the final frame (at the end of the ID3 tag), making the size |
| 39 | * of all the frames together smaller than the size given in the head of the tag. A possible purpose of this padding is |
| 40 | * to allow for adding a few additional frames or enlarge existing frames within the tag without having to rewrite the |
| 41 | * entire file. The value of the padding bytes must be $00. </p> <p class=t> The ID3v2 tag header, which should be the |
| 42 | * first information in the file, is 10 bytes as follows: </p> |
| 43 | * <p/> |
| 44 | * <p><center> <table border=0> <tr><td nowrap>ID3v2/file identifier</td><td rowspan=4> </td><td |
| 45 | * width="100%">"ID3"</td></tr> <tr><td>ID3v2 version</td><td>$03 00</td></tr> <tr><td>ID3v2 |
| 46 | * flags</td><td>%abc00000</td></tr> <tr><td>ID3v2 size</td><td>4 * %0xxxxxxx</td></tr> </table> </center> |
| 47 | * <p/> |
| 48 | * <p class=t> The first three bytes of the tag are always "ID3" to indicate that this is an ID3v2 tag, directly |
| 49 | * followed by the two version bytes. The first byte of ID3v2 version is it's major version, while the second byte is |
| 50 | * its revision number. In this case this is ID3v2.3.0. All revisions are backwards compatible while major versions are |
| 51 | * not. If software with ID3v2.2.0 and below support should encounter version three or higher it should simply ignore |
| 52 | * the whole tag. Version and revision will never be $FF. </p> |
| 53 | * <p/> |
| 54 | * <p class=t> The version is followed by one the ID3v2 flags field, of which currently only three flags are used. </p> |
| 55 | * <p/> |
| 56 | * <p class=t> a - Unsynchronisation </p> |
| 57 | * <p/> |
| 58 | * <p class=ind> Bit 7 in the 'ID3v2 flags' indicates whether or not unsynchronisation is used (see <a |
| 59 | * href="#sec5">section 5</a> for details); a set bit indicates usage.</p> |
| 60 | * <p/> |
| 61 | * <p class=t> b - Extended header </p> |
| 62 | * <p/> |
| 63 | * <p class=ind> The second bit (bit 6) indicates whether or not the header is followed by an extended header. The |
| 64 | * extended header is described in <a href="#sec3.2">section 3.2</a>. </p> |
| 65 | * <p/> |
| 66 | * <p class=t> c - Experimental indicator </p> |
| 67 | * <p/> |
| 68 | * <p class=ind> The third bit (bit 5) should be used as an 'experimental indicator'. This flag should always be set |
| 69 | * when the tag is in an experimental stage. </p> |
| 70 | * <p/> |
| 71 | * <p class=t> All the other flags should be cleared. If one of these undefined flags are set that might mean that the |
| 72 | * tag is not readable for a parser that does not know the flags function. </p> |
| 73 | * <p/> |
| 74 | * <p class=t> The ID3v2 tag size is encoded with four bytes where the most significant bit (bit 7) is set to zero in |
| 75 | * every byte, making a total of 28 bits. The zeroed bits are ignored, so a 257 bytes long tag is represented as $00 00 |
| 76 | * 02 01. </p> |
| 77 | * <p/> |
| 78 | * <p class=t> The ID3v2 tag size is the size of the complete tag after unsychronisation, including padding, excluding |
| 79 | * the header but not excluding the extended header (total tag size - 10). Only 28 bits (representing up to 256MB) are |
| 80 | * used in the size description to avoid the introducuction of 'false syncsignals'. </p> |
| 81 | * <p/> |
| 82 | * <p class=t> An ID3v2 tag can be detected with the following pattern:<br> $49 44 33 yy yy xx zz zz zz zz<br> Where yy |
| 83 | * is less than $FF, xx is the 'flags' byte and zz is less than $80. </p> |
| 84 | * |
| 85 | * @author Eric Farng |
| 86 | * @version $Revision: 1.5 $ |
| 87 | */ |
| 88 | public class ID3v2_3 extends ID3v2_2 { |
| 89 | |
| 90 | protected boolean crcDataFlag = false; |
| 91 | protected boolean experimental = false; |
| 92 | protected boolean extended = false; |
| 93 | protected int crcData = 0; |
| 94 | protected int paddingSize = 0; |
| 95 | |
| 96 | /** |
| 97 | * Creates a new ID3v2_3 object. |
| 98 | */ |
| 99 | public ID3v2_3() { |
| 100 | setMajorVersion((byte) 2); |
| 101 | setRevision((byte) 3); |
| 102 | } |
| 103 | |
| 104 | /** |
| 105 | * Creates a new ID3v2_3 object. |
| 106 | */ |
| 107 | public ID3v2_3(final ID3v2_3 copyObject) { |
| 108 | super(copyObject); |
| 109 | this.crcDataFlag = copyObject.crcDataFlag; |
| 110 | this.experimental = copyObject.experimental; |
| 111 | this.extended = copyObject.extended; |
| 112 | this.crcData = copyObject.crcData; |
| 113 | this.paddingSize = copyObject.paddingSize; |
| 114 | } |
| 115 | |
| 116 | /** |
| 117 | * Creates a new ID3v2_3 object. |
| 118 | */ |
| 119 | public ID3v2_3(final AbstractMP3Tag mp3tag) { |
| 120 | if (mp3tag != null) { |
| 121 | final ID3v2_4 convertedTag; |
| 122 | if ((mp3tag instanceof ID3v2_4 == false) && (mp3tag instanceof ID3v2_3 == true)) { |
| 123 | throw new UnsupportedOperationException("Copy Constructor not called. Please type cast the argument"); |
| 124 | } |
| 125 | if (mp3tag instanceof ID3v2_4) { |
| 126 | convertedTag = (ID3v2_4) mp3tag; |
| 127 | } else { |
| 128 | convertedTag = new ID3v2_4(mp3tag); |
| 129 | } |
| 130 | this.extended = convertedTag.extended; |
| 131 | this.experimental = convertedTag.experimental; |
| 132 | this.crcDataFlag = convertedTag.crcDataFlag; |
| 133 | this.crcData = convertedTag.crcData; |
| 134 | this.paddingSize = convertedTag.paddingSize; |
| 135 | this.compression = convertedTag.compression; |
| 136 | this.unsynchronization = convertedTag.unsynchronization; |
| 137 | final AbstractID3v2 id3tag = convertedTag; |
| 138 | final Iterator iterator = id3tag.getFrameIterator(); |
| 139 | AbstractID3v2Frame frame; |
| 140 | ID3v2_3Frame newFrame; |
| 141 | while (iterator.hasNext()) { |
| 142 | frame = (AbstractID3v2Frame) iterator.next(); |
| 143 | newFrame = new ID3v2_3Frame(frame); |
| 144 | this.setFrame(newFrame); |
| 145 | } |
| 146 | } |
| 147 | } |
| 148 | |
| 149 | /** |
| 150 | * Creates a new ID3v2_3 object. |
| 151 | */ |
| 152 | public ID3v2_3(final RandomAccessFile file) throws TagException, IOException { |
| 153 | this.read(file); |
| 154 | } |
| 155 | |
| 156 | public String getIdentifier() { |
| 157 | return "ID3v2.30"; |
| 158 | } |
| 159 | |
| 160 | public int getSize() { |
| 161 | int size = 3 + 2 + 1 + 4; |
| 162 | if (this.extended) { |
| 163 | if (this.crcDataFlag) { |
| 164 | size += (4 + 2 + 4 + 4); |
| 165 | } else { |
| 166 | size += (4 + 2 + 4); |
| 167 | } |
| 168 | } |
| 169 | final Iterator iterator = this.getFrameIterator(); |
| 170 | AbstractID3v2Frame frame; |
| 171 | while (iterator.hasNext()) { |
| 172 | frame = (AbstractID3v2Frame) iterator.next(); |
| 173 | size += frame.getSize(); |
| 174 | } |
| 175 | return size; |
| 176 | } |
| 177 | |
| 178 | public void append(final AbstractMP3Tag tag) { |
| 179 | if (tag instanceof ID3v2_3) { |
| 180 | this.experimental = ((ID3v2_3) tag).experimental; |
| 181 | this.extended = ((ID3v2_3) tag).extended; |
| 182 | this.crcDataFlag = ((ID3v2_3) tag).crcDataFlag; |
| 183 | this.paddingSize = ((ID3v2_3) tag).paddingSize; |
| 184 | this.crcData = ((ID3v2_3) tag).crcData; |
| 185 | } |
| 186 | super.append(tag); |
| 187 | } |
| 188 | |
| 189 | public boolean equals(final Object obj) { |
| 190 | if ((obj instanceof ID3v2_3) == false) { |
| 191 | return false; |
| 192 | } |
| 193 | final ID3v2_3 id3v2_3 = (ID3v2_3) obj; |
| 194 | if (this.crcData != id3v2_3.crcData) { |
| 195 | return false; |
| 196 | } |
| 197 | if (this.crcDataFlag != id3v2_3.crcDataFlag) { |
| 198 | return false; |
| 199 | } |
| 200 | if (this.experimental != id3v2_3.experimental) { |
| 201 | return false; |
| 202 | } |
| 203 | if (this.extended != id3v2_3.extended) { |
| 204 | return false; |
| 205 | } |
| 206 | if (this.paddingSize != id3v2_3.paddingSize) { |
| 207 | return false; |
| 208 | } |
| 209 | return super.equals(obj); |
| 210 | } |
| 211 | |
| 212 | public void overwrite(final AbstractMP3Tag tag) { |
| 213 | if (tag instanceof ID3v2_3) { |
| 214 | this.experimental = ((ID3v2_3) tag).experimental; |
| 215 | this.extended = ((ID3v2_3) tag).extended; |
| 216 | this.crcDataFlag = ((ID3v2_3) tag).crcDataFlag; |
| 217 | this.paddingSize = ((ID3v2_3) tag).paddingSize; |
| 218 | this.crcData = ((ID3v2_3) tag).crcData; |
| 219 | } |
| 220 | super.overwrite(tag); |
| 221 | } |
| 222 | |
| 223 | public void read(final RandomAccessFile file) throws TagException, IOException { |
| 224 | final int size; |
| 225 | final byte[] buffer = new byte[4]; |
| 226 | if (seek(file) == false) { |
| 227 | throw new TagNotFoundException(getIdentifier() + " tag not found"); |
| 228 | } |
| 229 | |
| 230 | // read the major and minor @version number & flags byte |
| 231 | file.read(buffer, 0, 3); |
| 232 | if ((buffer[0] != 3) || (buffer[1] != 0)) { |
| 233 | throw new TagNotFoundException(getIdentifier() + " tag not found"); |
| 234 | } |
| 235 | setMajorVersion(buffer[0]); |
| 236 | setRevision(buffer[1]); |
| 237 | this.unsynchronization = (buffer[2] & TagConstant.MASK_V23_UNSYNCHRONIZATION) != 0; |
| 238 | this.extended = (buffer[2] & TagConstant.MASK_V23_EXTENDED_HEADER) != 0; |
| 239 | this.experimental = (buffer[2] & TagConstant.MASK_V23_EXPERIMENTAL) != 0; |
| 240 | |
| 241 | // read the size |
| 242 | file.read(buffer, 0, 4); |
| 243 | size = byteArrayToSize(buffer); |
| 244 | final long filePointer = file.getFilePointer(); |
| 245 | if (this.extended) { |
| 246 | // int is 4 bytes. |
| 247 | final int extendedHeaderSize = file.readInt(); |
| 248 | |
| 249 | // the extended header is only 6 or 10 bytes. |
| 250 | if (extendedHeaderSize != 6 && extendedHeaderSize != 10) { |
| 251 | throw new InvalidTagException("Invalid Extended Header Size."); |
| 252 | } |
| 253 | file.read(buffer, 0, 2); |
| 254 | this.crcDataFlag = (buffer[0] & TagConstant.MASK_V23_CRC_DATA_PRESENT) != 0; |
| 255 | |
| 256 | // if it's 10 bytes, the CRC flag must be set |
| 257 | // and if it's 6 bytes, it must not be set |
| 258 | if (((extendedHeaderSize == 10) && (this.crcDataFlag == false)) || |
| 259 | ((extendedHeaderSize == 6) && (this.crcDataFlag == true))) { |
| 260 | throw new InvalidTagException("CRC Data flag not set correctly."); |
| 261 | } |
| 262 | this.paddingSize = file.readInt(); |
| 263 | if ((extendedHeaderSize == 10) && this.crcDataFlag) { |
| 264 | this.crcData = file.readInt(); |
| 265 | } |
| 266 | } |
| 267 | ID3v2_3Frame next; |
| 268 | this.clearFrameMap(); |
| 269 | |
| 270 | // read all the frames. |
| 271 | this.setFileReadBytes(size); |
| 272 | AbstractID3v2.resetPaddingCounter(); |
| 273 | while ((file.getFilePointer() - filePointer) <= size) { |
| 274 | try { |
| 275 | next = new ID3v2_3Frame(file); |
| 276 | final String id = next.getIdentifier(); |
| 277 | if (this.hasFrame(id)) { |
| 278 | this.appendDuplicateFrameId(id + "; "); |
| 279 | this.incrementDuplicateBytes(this.getFrame(id).getSize()); |
| 280 | } |
| 281 | this.setFrame(next); |
| 282 | } catch (InvalidTagException ex) { |
| 283 | if (ex.getMessage().equals("Found empty frame")) { |
| 284 | this.incrementEmptyFrameBytes(10); |
| 285 | } else { |
| 286 | this.incrementInvalidFrameBytes(); |
| 287 | } |
| 288 | } |
| 289 | } |
| 290 | this.setPaddingSize(getPaddingCounter()); |
| 291 | } |
| 292 | |
| 293 | public boolean seek(final RandomAccessFile file) throws IOException { |
| 294 | final byte[] buffer = new byte[3]; |
| 295 | file.seek(0); |
| 296 | |
| 297 | // read the tag if it exists |
| 298 | file.read(buffer, 0, 3); |
| 299 | final String tag = new String(buffer, 0, 3); |
| 300 | if (tag.equals("ID3") == false) { |
| 301 | return false; |
| 302 | } |
| 303 | |
| 304 | // read the major and minor @version number |
| 305 | file.read(buffer, 0, 2); |
| 306 | |
| 307 | // read back the @version bytes so we can read and save them later |
| 308 | file.seek(file.getFilePointer() - 2); |
| 309 | return ((buffer[0] == 3) && (buffer[1] == 0)); |
| 310 | } |
| 311 | |
| 312 | public String toString() { |
| 313 | final Iterator iterator = this.getFrameIterator(); |
| 314 | AbstractID3v2Frame frame; |
| 315 | String str = getIdentifier() + " " + this.getSize() + "\n"; |
| 316 | str += ("compression = " + this.compression + "\n"); |
| 317 | str += ("unsynchronization = " + this.unsynchronization + "\n"); |
| 318 | str += ("crcData = " + this.crcData + "\n"); |
| 319 | str += ("crcDataFlag = " + this.crcDataFlag + "\n"); |
| 320 | str += ("experimental = " + this.experimental + "\n"); |
| 321 | str += ("extended = " + this.extended + "\n"); |
| 322 | str += ("paddingSize = " + this.paddingSize + "\n"); |
| 323 | while (iterator.hasNext()) { |
| 324 | frame = (ID3v2_3Frame) iterator.next(); |
| 325 | str += (frame.toString() + "\n"); |
| 326 | } |
| 327 | return str + "\n"; |
| 328 | } |
| 329 | |
| 330 | public void write(final AbstractMP3Tag tag) { |
| 331 | if (tag instanceof ID3v2_3) { |
| 332 | this.experimental = ((ID3v2_3) tag).experimental; |
| 333 | this.extended = ((ID3v2_3) tag).extended; |
| 334 | this.crcDataFlag = ((ID3v2_3) tag).crcDataFlag; |
| 335 | this.paddingSize = ((ID3v2_3) tag).paddingSize; |
| 336 | this.crcData = ((ID3v2_3) tag).crcData; |
| 337 | } |
| 338 | super.write(tag); |
| 339 | } |
| 340 | |
| 341 | public void write(final RandomAccessFile file) throws IOException { |
| 342 | final String str; |
| 343 | final Iterator iterator; |
| 344 | final byte[] buffer = new byte[6]; |
| 345 | final MP3File mp3 = new MP3File(); |
| 346 | mp3.seekMP3Frame(file); |
| 347 | final long mp3start = file.getFilePointer(); |
| 348 | file.seek(0); |
| 349 | ID3v2_3Frame frame; |
| 350 | str = "ID3"; |
| 351 | for (int i = 0; i < str.length(); i++) { |
| 352 | buffer[i] = (byte) str.charAt(i); |
| 353 | } |
| 354 | buffer[3] = 3; |
| 355 | buffer[4] = 0; |
| 356 | if (this.unsynchronization) { |
| 357 | buffer[5] |= TagConstant.MASK_V23_UNSYNCHRONIZATION; |
| 358 | } |
| 359 | if (this.extended) { |
| 360 | buffer[5] |= TagConstant.MASK_V23_EXTENDED_HEADER; |
| 361 | } |
| 362 | if (this.experimental) { |
| 363 | buffer[5] |= TagConstant.MASK_V23_EXPERIMENTAL; |
| 364 | } |
| 365 | file.write(buffer); |
| 366 | |
| 367 | // write size |
| 368 | file.write(sizeToByteArray((int) mp3start - 10)); |
| 369 | if (this.extended) { |
| 370 | if (this.crcDataFlag) { |
| 371 | file.writeInt(10); |
| 372 | buffer[0] = 0; |
| 373 | buffer[0] |= TagConstant.MASK_V23_CRC_DATA_PRESENT; |
| 374 | file.write(buffer, 0, 2); |
| 375 | file.writeInt(this.paddingSize); |
| 376 | file.writeInt(this.crcData); |
| 377 | } else { |
| 378 | file.writeInt(6); |
| 379 | file.write(buffer, 0, 2); |
| 380 | file.writeInt(this.paddingSize); |
| 381 | } |
| 382 | } |
| 383 | |
| 384 | // write all frames |
| 385 | iterator = this.getFrameIterator(); |
| 386 | while (iterator.hasNext()) { |
| 387 | frame = (ID3v2_3Frame) iterator.next(); |
| 388 | frame.write(file); |
| 389 | } |
| 390 | } |
| 391 | |
| 392 | public String getSongTitle() { |
| 393 | String text = ""; |
| 394 | AbstractID3v2Frame frame = getFrame("TIT2"); |
| 395 | if (frame != null) { |
| 396 | FrameBodyTIT2 body = (FrameBodyTIT2) frame.getBody(); |
| 397 | text = body.getText(); |
| 398 | } |
| 399 | return text.trim(); |
| 400 | } |
| 401 | |
| 402 | public String getLeadArtist() { |
| 403 | String text = ""; |
| 404 | AbstractID3v2Frame frame = getFrame("TPE1"); |
| 405 | if (frame != null) { |
| 406 | FrameBodyTPE1 body = (FrameBodyTPE1) frame.getBody(); |
| 407 | text = body.getText(); |
| 408 | } |
| 409 | return text.trim(); |
| 410 | } |
| 411 | |
| 412 | public String getAlbumTitle() { |
| 413 | String text = ""; |
| 414 | AbstractID3v2Frame frame = getFrame("TALB"); |
| 415 | if (frame != null) { |
| 416 | FrameBodyTALB body = (FrameBodyTALB) frame.getBody(); |
| 417 | text = body.getText(); |
| 418 | } |
| 419 | return text.trim(); |
| 420 | } |
| 421 | |
| 422 | public String getYearReleased() { |
| 423 | String text = ""; |
| 424 | AbstractID3v2Frame frame = getFrame("TYER"); |
| 425 | if (frame != null) { |
| 426 | FrameBodyTYER body = (FrameBodyTYER) frame.getBody(); |
| 427 | text = body.getText(); |
| 428 | } |
| 429 | return text.trim(); |
| 430 | } |
| 431 | |
| 432 | public String getSongComment() { |
| 433 | String text = ""; |
| 434 | AbstractID3v2Frame frame = getFrame("COMM" + ((char) 0) + "eng" + ((char) 0) + ""); |
| 435 | if (frame != null) { |
| 436 | FrameBodyCOMM body = (FrameBodyCOMM) frame.getBody(); |
| 437 | text = body.getText(); |
| 438 | } |
| 439 | return text.trim(); |
| 440 | } |
| 441 | |
| 442 | public String getSongGenre() { |
| 443 | String text = ""; |
| 444 | AbstractID3v2Frame frame = getFrame("TCON"); |
| 445 | if (frame != null) { |
| 446 | FrameBodyTCON body = (FrameBodyTCON) frame.getBody(); |
| 447 | text = body.getText(); |
| 448 | } |
| 449 | return text.trim(); |
| 450 | } |
| 451 | |
| 452 | public String getTrackNumberOnAlbum() { |
| 453 | String text = ""; |
| 454 | AbstractID3v2Frame frame = getFrame("TRCK"); |
| 455 | if (frame != null) { |
| 456 | FrameBodyTRCK body = (FrameBodyTRCK) frame.getBody(); |
| 457 | text = body.getText(); |
| 458 | } |
| 459 | return text.trim(); |
| 460 | } |
| 461 | |
| 462 | public String getSongLyric() { |
| 463 | String text = ""; |
| 464 | AbstractID3v2Frame frame = getFrame("SYLT"); |
| 465 | if (frame != null) { |
| 466 | FrameBodySYLT body = (FrameBodySYLT) frame.getBody(); |
| 467 | text = body.getLyric(); |
| 468 | } |
| 469 | if (text == "") { |
| 470 | frame = getFrame("USLT" + ((char) 0) + "eng" + ((char) 0) + ""); |
| 471 | if (frame != null) { |
| 472 | FrameBodyUSLT body = (FrameBodyUSLT) frame.getBody(); |
| 473 | text = body.getLyric(); |
| 474 | } |
| 475 | } |
| 476 | return text.trim(); |
| 477 | } |
| 478 | |
| 479 | public String getAuthorComposer() { |
| 480 | String text = ""; |
| 481 | AbstractID3v2Frame frame = getFrame("TCOM"); |
| 482 | if (frame != null) { |
| 483 | FrameBodyTCOM body = (FrameBodyTCOM) frame.getBody(); |
| 484 | text = body.getText(); |
| 485 | } |
| 486 | return text.trim(); |
| 487 | } |
| 488 | |
| 489 | public void setSongTitle(String songTitle) { |
| 490 | AbstractID3v2Frame field = getFrame("TIT2"); |
| 491 | if (field == null) { |
| 492 | field = new ID3v2_3Frame(new FrameBodyTIT2((byte) 0, songTitle.trim())); |
| 493 | setFrame(field); |
| 494 | } else { |
| 495 | ((FrameBodyTIT2) field.getBody()).setText(songTitle.trim()); |
| 496 | } |
| 497 | } |
| 498 | |
| 499 | public void setLeadArtist(String leadArtist) { |
| 500 | AbstractID3v2Frame field = getFrame("TPE1"); |
| 501 | if (field == null) { |
| 502 | field = new ID3v2_3Frame(new FrameBodyTPE1((byte) 0, leadArtist.trim())); |
| 503 | setFrame(field); |
| 504 | } else { |
| 505 | ((FrameBodyTPE1) field.getBody()).setText(leadArtist.trim()); |
| 506 | } |
| 507 | } |
| 508 | |
| 509 | public void setAlbumTitle(String albumTitle) { |
| 510 | AbstractID3v2Frame field = getFrame("TALB"); |
| 511 | if (field == null) { |
| 512 | field = new ID3v2_3Frame(new FrameBodyTALB((byte) 0, albumTitle.trim())); |
| 513 | setFrame(field); |
| 514 | } else { |
| 515 | ((FrameBodyTALB) field.getBody()).setText(albumTitle.trim()); |
| 516 | } |
| 517 | } |
| 518 | |
| 519 | public void setYearReleased(String yearReleased) { |
| 520 | AbstractID3v2Frame field = getFrame("TYER"); |
| 521 | if (field == null) { |
| 522 | field = new ID3v2_3Frame(new FrameBodyTYER((byte) 0, yearReleased.trim())); |
| 523 | setFrame(field); |
| 524 | } else { |
| 525 | ((FrameBodyTYER) field.getBody()).setText(yearReleased.trim()); |
| 526 | } |
| 527 | } |
| 528 | |
| 529 | public void setSongComment(String songComment) { |
| 530 | AbstractID3v2Frame field = getFrame("COMM"); |
| 531 | if (field == null) { |
| 532 | field = new ID3v2_3Frame(new FrameBodyCOMM((byte) 0, "ENG", "", songComment.trim())); |
| 533 | setFrame(field); |
| 534 | } else { |
| 535 | ((FrameBodyCOMM) field.getBody()).setText(songComment.trim()); |
| 536 | } |
| 537 | } |
| 538 | |
| 539 | public void setSongGenre(String songGenre) { |
| 540 | AbstractID3v2Frame field = getFrame("TCON"); |
| 541 | if (field == null) { |
| 542 | field = new ID3v2_3Frame(new FrameBodyTCON((byte) 0, songGenre.trim())); |
| 543 | setFrame(field); |
| 544 | } else { |
| 545 | ((FrameBodyTCON) field.getBody()).setText(songGenre.trim()); |
| 546 | } |
| 547 | } |
| 548 | |
| 549 | public void setTrackNumberOnAlbum(String trackNumberOnAlbum) { |
| 550 | AbstractID3v2Frame field = getFrame("TRCK"); |
| 551 | if (field == null) { |
| 552 | field = new ID3v2_3Frame(new FrameBodyTRCK((byte) 0, trackNumberOnAlbum.trim())); |
| 553 | setFrame(field); |
| 554 | } else { |
| 555 | ((FrameBodyTRCK) field.getBody()).setText(trackNumberOnAlbum.trim()); |
| 556 | } |
| 557 | } |
| 558 | |
| 559 | public void setSongLyric(String songLyrics) { |
| 560 | AbstractID3v2Frame field = getFrame("SYLT"); |
| 561 | if (field == null) { |
| 562 | field = new ID3v2_3Frame(new FrameBodyUSLT((byte) 0, "ENG", "", songLyrics.trim())); |
| 563 | setFrame(field); |
| 564 | } else { |
| 565 | ((FrameBodyUSLT) field.getBody()).setLyric(songLyrics.trim()); |
| 566 | } |
| 567 | } |
| 568 | |
| 569 | public void setAuthorComposer(String authorComposer) { |
| 570 | AbstractID3v2Frame field = getFrame("TCOM"); |
| 571 | if (field == null) { |
| 572 | field = new ID3v2_3Frame(new FrameBodyTCOM((byte) 0, authorComposer.trim())); |
| 573 | setFrame(field); |
| 574 | } else { |
| 575 | ((FrameBodyTCOM) field.getBody()).setText(authorComposer.trim()); |
| 576 | } |
| 577 | } |
| 578 | } |