2
* MusicTag Copyright (C)2003,2004
4
* This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser
5
* General Public License as published by the Free Software Foundation; either version 2.1 of the License,
6
* or (at your option) any later version.
8
* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
9
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10
* See the GNU Lesser General Public License for more details.
12
* You should have received a copy of the GNU Lesser General Public License along with this library; if not,
13
* you can get a copy from http://www.opensource.org/licenses/lgpl-license.php or write to the Free Software
14
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16
package org.jaudiotagger.tag.id3;
18
import org.jaudiotagger.FileConstants;
19
import org.jaudiotagger.audio.mp3.MP3File;
20
import org.jaudiotagger.tag.*;
21
import org.jaudiotagger.tag.id3.framebody.*;
22
import org.jaudiotagger.tag.id3.valuepair.GenreTypes;
23
import org.jaudiotagger.tag.lyrics3.AbstractLyrics3;
24
import org.jaudiotagger.tag.lyrics3.Lyrics3v2;
25
import org.jaudiotagger.tag.lyrics3.Lyrics3v2Field;
28
import java.io.IOException;
29
import java.io.RandomAccessFile;
30
import java.io.UnsupportedEncodingException;
31
import java.nio.ByteBuffer;
32
import java.nio.channels.FileChannel;
33
import java.nio.channels.WritableByteChannel;
35
import java.util.logging.Level;
38
* Represents an ID3v2.4 tag.
40
* @author : Paul Taylor
41
* @author : Eric Farng
42
* @version $Id: ID3v24Tag.java,v 1.39 2007/12/03 13:28:04 paultaylor Exp $
44
public class ID3v24Tag extends AbstractID3v2Tag
46
protected static final String TYPE_FOOTER = "footer";
47
protected static final String TYPE_IMAGEENCODINGRESTRICTION = "imageEncodingRestriction";
48
protected static final String TYPE_IMAGESIZERESTRICTION = "imageSizeRestriction";
49
protected static final String TYPE_TAGRESTRICTION = "tagRestriction";
50
protected static final String TYPE_TAGSIZERESTRICTION = "tagSizeRestriction";
51
protected static final String TYPE_TEXTENCODINGRESTRICTION = "textEncodingRestriction";
52
protected static final String TYPE_TEXTFIELDSIZERESTRICTION = "textFieldSizeRestriction";
53
protected static final String TYPE_UPDATETAG = "updateTag";
54
protected static final String TYPE_CRCDATA = "crcdata";
55
protected static final String TYPE_EXPERIMENTAL = "experimental";
56
protected static final String TYPE_EXTENDED = "extended";
57
protected static final String TYPE_PADDINGSIZE = "paddingsize";
58
protected static final String TYPE_UNSYNCHRONISATION = "unsyncronisation";
61
protected static int TAG_EXT_HEADER_LENGTH = 6;
62
protected static int TAG_EXT_HEADER_UPDATE_LENGTH = 1;
63
protected static int TAG_EXT_HEADER_CRC_LENGTH = 6;
64
protected static int TAG_EXT_HEADER_RESTRICTION_LENGTH = 2;
65
protected static int TAG_EXT_HEADER_CRC_DATA_LENGTH = 5;
66
protected static int TAG_EXT_HEADER_RESTRICTION_DATA_LENGTH = 1;
67
protected static int TAG_EXT_NUMBER_BYTES_DATA_LENGTH = 1;
70
* ID3v2.4 Header bit mask
72
public static final int MASK_V24_UNSYNCHRONIZATION = FileConstants.BIT7;
75
* ID3v2.4 Header bit mask
77
public static final int MASK_V24_EXTENDED_HEADER = FileConstants.BIT6;
80
* ID3v2.4 Header bit mask
82
public static final int MASK_V24_EXPERIMENTAL = FileConstants.BIT5;
85
* ID3v2.4 Header bit mask
87
public static final int MASK_V24_FOOTER_PRESENT = FileConstants.BIT4;
90
* ID3v2.4 Extended header bit mask
92
public static final int MASK_V24_TAG_UPDATE = FileConstants.BIT6;
95
* ID3v2.4 Extended header bit mask
97
public static final int MASK_V24_CRC_DATA_PRESENT = FileConstants.BIT5;
100
* ID3v2.4 Extended header bit mask
102
public static final int MASK_V24_TAG_RESTRICTIONS = FileConstants.BIT4;
105
* ID3v2.4 Extended header bit mask
107
public static final int MASK_V24_TAG_SIZE_RESTRICTIONS = (byte) FileConstants.BIT7 | FileConstants.BIT6;
110
* ID3v2.4 Extended header bit mask
112
public static final int MASK_V24_TEXT_ENCODING_RESTRICTIONS = FileConstants.BIT5;
115
* ID3v2.4 Extended header bit mask
117
public static final int MASK_V24_TEXT_FIELD_SIZE_RESTRICTIONS = FileConstants.BIT4 | FileConstants.BIT3;
120
* ID3v2.4 Extended header bit mask
122
public static final int MASK_V24_IMAGE_ENCODING = FileConstants.BIT2;
125
* ID3v2.4 Extended header bit mask
127
public static final int MASK_V24_IMAGE_SIZE_RESTRICTIONS = FileConstants.BIT2 | FileConstants.BIT1;
130
* ID3v2.4 Header Footer are the same as the header flags. WHY?!?! move the
131
* flags from thier position in 2.3??????????
134
* ID3v2.4 Header Footer bit mask
136
public static final int MASK_V24_TAG_ALTER_PRESERVATION = FileConstants.BIT6;
139
* ID3v2.4 Header Footer bit mask
141
public static final int MASK_V24_FILE_ALTER_PRESERVATION = FileConstants.BIT5;
144
* ID3v2.4 Header Footer bit mask
146
public static final int MASK_V24_READ_ONLY = FileConstants.BIT4;
149
* ID3v2.4 Header Footer bit mask
151
public static final int MASK_V24_GROUPING_IDENTITY = FileConstants.BIT6;
154
* ID3v2.4 Header Footer bit mask
156
public static final int MASK_V24_COMPRESSION = FileConstants.BIT4;
159
* ID3v2.4 Header Footer bit mask
161
public static final int MASK_V24_ENCRYPTION = FileConstants.BIT3;
164
* ID3v2.4 Header Footer bit mask
166
public static final int MASK_V24_FRAME_UNSYNCHRONIZATION = FileConstants.BIT2;
169
* ID3v2.4 Header Footer bit mask
171
public static final int MASK_V24_DATA_LENGTH_INDICATOR = FileConstants.BIT1;
174
* CRC Checksum calculated
176
protected boolean crcDataFlag = false;
181
protected boolean experimental = false;
184
* Contains extended header
186
protected boolean extended = false;
189
* All frames in the tag uses unsynchronisation
191
protected boolean unsynchronization = false;
196
protected int crcData = 0;
202
protected boolean footer = false;
207
protected boolean updateTag = false;
210
* Tag has restrictions
212
protected boolean tagRestriction = false;
217
protected byte imageEncodingRestriction = 0;
222
protected byte imageSizeRestriction = 0;
227
protected byte tagSizeRestriction = 0;
232
protected byte textEncodingRestriction = 0;
237
protected int paddingSize = 0;
243
protected byte textFieldSizeRestriction = 0;
245
public static final byte RELEASE = 2;
246
public static final byte MAJOR_VERSION = 4;
247
public static final byte REVISION = 0;
250
* Retrieve the Release
252
public byte getRelease()
258
* Retrieve the Major Version
260
public byte getMajorVersion()
262
return MAJOR_VERSION;
266
* Retrieve the Revision
268
public byte getRevision()
275
* Creates a new empty ID3v2_4 datatype.
279
frameMap = new LinkedHashMap();
283
* Copy primitives applicable to v2.4, this is used when cloning a v2.4 datatype
284
* and other objects such as v2.3 so need to check instanceof
286
protected void copyPrimitives(AbstractID3v2Tag copyObj)
288
logger.info("Copying primitives");
289
super.copyPrimitives(copyObj);
291
if (copyObj instanceof ID3v24Tag)
293
ID3v24Tag copyObject = (ID3v24Tag) copyObj;
294
this.footer = copyObject.footer;
295
this.tagRestriction = copyObject.tagRestriction;
296
this.updateTag = copyObject.updateTag;
297
this.imageEncodingRestriction = copyObject.imageEncodingRestriction;
298
this.imageSizeRestriction = copyObject.imageSizeRestriction;
299
this.tagSizeRestriction = copyObject.tagSizeRestriction;
300
this.textEncodingRestriction = copyObject.textEncodingRestriction;
301
this.textFieldSizeRestriction = copyObject.textFieldSizeRestriction;
306
* Copy frames from one tag into a v2.4 tag
308
protected void copyFrames(AbstractID3v2Tag copyObject)
310
logger.info("Copying Frames,there are:" + copyObject.frameMap.keySet().size() + " different types");
311
frameMap = new LinkedHashMap();
312
//Copy Frames that are a valid 2.4 type
313
Iterator iterator = copyObject.frameMap.keySet().iterator();
314
AbstractID3v2Frame frame;
315
ID3v24Frame newFrame = null;
316
while (iterator.hasNext())
318
String id = (String) iterator.next();
319
Object o = copyObject.frameMap.get(id);
321
if (o instanceof AbstractID3v2Frame)
323
frame = (AbstractID3v2Frame) o;
326
newFrame = new ID3v24Frame(frame);
327
logger.info("Adding Frame:" + newFrame.getIdentifier());
328
copyFrameIntoMap(newFrame.getIdentifier(), newFrame);
330
catch (InvalidFrameException ife)
332
logger.log(Level.SEVERE, "Unable to convert frame:" + frame.getIdentifier(), ife);
336
else if (o instanceof ArrayList)
338
ArrayList multiFrame = new ArrayList();
339
for (ListIterator li = ((ArrayList) o).listIterator(); li.hasNext();)
341
frame = (AbstractID3v2Frame) li.next();
344
newFrame = new ID3v24Frame(frame);
345
multiFrame.add(newFrame);
347
catch (InvalidFrameException ife)
349
logger.log(Level.SEVERE, "Unable to convert frame:" + frame.getIdentifier());
352
if (newFrame != null)
354
logger.finest("Adding multi frame list to map:" + newFrame.getIdentifier());
355
frameMap.put(newFrame.getIdentifier(), multiFrame);
362
* Copy Constructor, creates a new ID3v2_4 Tag based on another ID3v2_4 Tag
364
public ID3v24Tag(ID3v24Tag copyObject)
366
logger.info("Creating tag from another tag of same type");
367
copyPrimitives(copyObject);
368
copyFrames(copyObject);
372
* Creates a new ID3v2_4 datatype based on another (non 2.4) tag
376
public ID3v24Tag(AbstractTag mp3tag)
378
logger.info("Creating tag from a tag of a different version");
379
frameMap = new LinkedHashMap();
382
//Should use simpler copy constructor
383
if ((mp3tag instanceof ID3v24Tag == true))
385
throw new UnsupportedOperationException("Copy Constructor not called. Please type cast the argument");
387
/* If we get a tag, we want to convert to id3v2_4
388
* both id3v1 and lyrics3 convert to this type
389
* id3v1 needs to convert to id3v2_4 before converting to lyrics3
391
else if (mp3tag instanceof AbstractID3v2Tag)
393
copyPrimitives((AbstractID3v2Tag) mp3tag);
394
copyFrames((AbstractID3v2Tag) mp3tag);
397
else if (mp3tag instanceof ID3v1Tag)
399
// convert id3v1 tags.
400
ID3v1Tag id3tag = (ID3v1Tag) mp3tag;
401
ID3v24Frame newFrame;
402
AbstractID3v2FrameBody newBody;
403
if (id3tag.title.length() > 0)
405
newBody = new FrameBodyTIT2((byte) 0, id3tag.title);
406
newFrame = new ID3v24Frame(ID3v24Frames.FRAME_ID_TITLE);
407
newFrame.setBody(newBody);
408
frameMap.put(newFrame.getIdentifier(), newFrame);
410
if (id3tag.artist.length() > 0)
412
newBody = new FrameBodyTPE1((byte) 0, id3tag.artist);
413
newFrame = new ID3v24Frame(ID3v24Frames.FRAME_ID_ARTIST);
414
newFrame.setBody(newBody);
415
frameMap.put(newFrame.getIdentifier(), newFrame);
417
if (id3tag.album.length() > 0)
419
newBody = new FrameBodyTALB((byte) 0, id3tag.album);
420
newFrame = new ID3v24Frame(ID3v24Frames.FRAME_ID_ALBUM);
421
newFrame.setBody(newBody);
422
frameMap.put(newFrame.getIdentifier(), newFrame);
424
if (id3tag.year.length() > 0)
426
newBody = new FrameBodyTDRC((byte) 0, id3tag.year);
427
newFrame = new ID3v24Frame(ID3v24Frames.FRAME_ID_YEAR);
428
newFrame.setBody(newBody);
429
frameMap.put(newFrame.getIdentifier(), newFrame);
431
if (id3tag.comment.length() > 0)
433
newBody = new FrameBodyCOMM((byte) 0, "ENG", "", id3tag.comment);
434
newFrame = new ID3v24Frame(ID3v24Frames.FRAME_ID_COMMENT);
435
newFrame.setBody(newBody);
436
frameMap.put(newFrame.getIdentifier(), newFrame);
438
if (((id3tag.genre & ID3v1Tag.BYTE_TO_UNSIGNED) >= 0) && ((id3tag.genre & ID3v1Tag.BYTE_TO_UNSIGNED) != ID3v1Tag.BYTE_TO_UNSIGNED))
440
Integer genreId = id3tag.genre & ID3v1Tag.BYTE_TO_UNSIGNED;
441
String genre = "(" + genreId + ") " + GenreTypes.getInstanceOf().getValueForId(genreId);
443
newBody = new FrameBodyTCON((byte) 0, genre);
444
newFrame = new ID3v24Frame(ID3v24Frames.FRAME_ID_GENRE);
445
newFrame.setBody(newBody);
446
frameMap.put(newFrame.getIdentifier(), newFrame);
448
if (mp3tag instanceof ID3v11Tag)
450
ID3v11Tag id3tag2 = (ID3v11Tag) mp3tag;
451
if (id3tag2.track > 0)
453
newBody = new FrameBodyTRCK((byte) 0, Byte.toString(id3tag2.track));
454
newFrame = new ID3v24Frame(ID3v24Frames.FRAME_ID_TRACK);
455
newFrame.setBody(newBody);
456
frameMap.put(newFrame.getIdentifier(), newFrame);
461
else if (mp3tag instanceof AbstractLyrics3)
463
//Put the conversion stuff in the individual frame code.
465
if (mp3tag instanceof Lyrics3v2)
467
lyric = new Lyrics3v2((Lyrics3v2) mp3tag);
471
lyric = new Lyrics3v2(mp3tag);
473
Iterator iterator = lyric.iterator();
474
Lyrics3v2Field field;
475
ID3v24Frame newFrame;
476
while (iterator.hasNext())
480
field = (Lyrics3v2Field) iterator.next();
481
newFrame = new ID3v24Frame(field);
482
frameMap.put(newFrame.getIdentifier(), newFrame);
484
catch (InvalidTagException ex)
486
logger.warning("Unable to convert Lyrics3 to v24 Frame:Frame Identifier");
494
* Creates a new ID3v2_4 datatype.
497
* @param loggingFilename
498
* @throws TagException
500
public ID3v24Tag(ByteBuffer buffer, String loggingFilename) throws TagException
502
frameMap = new LinkedHashMap();
503
setLoggingFilename(loggingFilename);
509
* Creates a new ID3v2_4 datatype.
512
* @throws TagException
513
* @deprecated use {@link #ID3v24Tag(ByteBuffer,String)} instead
515
public ID3v24Tag(ByteBuffer buffer) throws TagException
523
public String getIdentifier()
529
* Return tag size based upon the sizes of the frames rather than the physical
530
* no of bytes between start of ID3Tag and start of Audio Data.
536
int size = TAG_HEADER_LENGTH;
539
size += this.TAG_EXT_HEADER_LENGTH;
542
size += this.TAG_EXT_HEADER_UPDATE_LENGTH;
546
size += this.TAG_EXT_HEADER_CRC_LENGTH;
550
size += this.TAG_EXT_HEADER_RESTRICTION_LENGTH;
553
size += super.getSize();
554
logger.finer("Tag Size is" + size);
562
public boolean equals(Object obj)
564
if ((obj instanceof ID3v24Tag) == false)
568
ID3v24Tag object = (ID3v24Tag) obj;
569
if (this.footer != object.footer)
573
if (this.imageEncodingRestriction != object.imageEncodingRestriction)
577
if (this.imageSizeRestriction != object.imageSizeRestriction)
581
if (this.tagRestriction != object.tagRestriction)
585
if (this.tagSizeRestriction != object.tagSizeRestriction)
589
if (this.textEncodingRestriction != object.textEncodingRestriction)
593
if (this.textFieldSizeRestriction != object.textFieldSizeRestriction)
597
if (this.updateTag != object.updateTag)
601
return super.equals(obj);
605
* Read Tag from Specified file.
606
* Read tag header, delegate reading of frames to readFrames()
608
* @param byteBuffer to read the tag from
609
* @throws TagException
610
* @throws TagNotFoundException
611
* @throws InvalidTagException
613
public void read(ByteBuffer byteBuffer) throws TagException
617
if (seek(byteBuffer) == false)
619
throw new TagNotFoundException(getLoggingFilename() + ":" + getIdentifier() + " tag not found");
622
byte flags = byteBuffer.get();
623
unsynchronization = (flags & MASK_V24_UNSYNCHRONIZATION) != 0;
624
extended = (flags & MASK_V24_EXTENDED_HEADER) != 0;
625
experimental = (flags & MASK_V24_EXPERIMENTAL) != 0;
626
footer = (flags & MASK_V24_FOOTER_PRESENT) != 0;
628
if (isUnsynchronization())
630
logger.warning(getLoggingFilename() + ":" + "ID3v24 Tag is unsynchronized");
635
logger.warning(getLoggingFilename() + ":" + "ID3v24 Tag is extended");
640
logger.warning(getLoggingFilename() + ":" + "ID3v24 Tag is experimental");
645
logger.warning(getLoggingFilename() + ":" + "ID3v24 Tag has footer");
648
// Read the size, this is size of tag apart from tag header
649
size = ID3SyncSafeInteger.bufferToValue(byteBuffer);
650
logger.info(getLoggingFilename() + ":" + "Reading tag from file size set in header is" + size);
654
int extendedHeaderSize = byteBuffer.getInt();
655
// the extended header must be atleast 6 bytes
656
if (extendedHeaderSize <= TAG_EXT_HEADER_LENGTH)
658
throw new InvalidTagException(getLoggingFilename() + ":" + "Invalid Extended Header Size.");
662
// Read the extended flag bytes
663
byte extFlag = byteBuffer.get();
664
updateTag = (extFlag & MASK_V24_TAG_UPDATE) != 0;
665
crcDataFlag = (extFlag & MASK_V24_CRC_DATA_PRESENT) != 0;
666
tagRestriction = (extFlag & MASK_V24_TAG_RESTRICTIONS) != 0;
667
// read the length byte if the flag is set
668
// this tag should always be zero but just in case
669
// read this information.
670
if (updateTag == true)
674
if (crcDataFlag == true)
676
// the CRC has a variable length
678
buffer = new byte[TAG_EXT_HEADER_CRC_DATA_LENGTH];
679
byteBuffer.get(buffer, 0, TAG_EXT_HEADER_CRC_DATA_LENGTH);
681
for (int i = 0; i < TAG_EXT_HEADER_CRC_DATA_LENGTH; i++)
684
crcData += buffer[i];
687
if (tagRestriction == true)
690
buffer = new byte[1];
691
byteBuffer.get(buffer, 0, 1);
692
tagSizeRestriction = (byte) ((buffer[0] & MASK_V24_TAG_SIZE_RESTRICTIONS) >> 6);
693
textEncodingRestriction = (byte) ((buffer[0] & MASK_V24_TEXT_ENCODING_RESTRICTIONS) >> 5);
694
textFieldSizeRestriction = (byte) ((buffer[0] & MASK_V24_TEXT_FIELD_SIZE_RESTRICTIONS) >> 3);
695
imageEncodingRestriction = (byte) ((buffer[0] & MASK_V24_IMAGE_ENCODING) >> 2);
696
imageSizeRestriction = (byte) (buffer[0] & MASK_V24_IMAGE_SIZE_RESTRICTIONS);
699
//Note if there was an extended header the size value has padding taken
700
//off so we dont search it.
701
readFrames(byteBuffer, size);
706
* Read frames from tag
708
protected void readFrames(ByteBuffer byteBuffer, int size)
710
logger.finest(getLoggingFilename() + ":" + "Start of frame body at" + byteBuffer.position());
711
//Now start looking for frames
713
frameMap = new LinkedHashMap();
714
//Read the size from the Tag Header
715
this.fileReadSize = size;
716
// Read the frames until got to upto the size as specified in header
717
logger.finest(getLoggingFilename() + ":" + "Start of frame body at:" + byteBuffer.position() + ",frames data size is:" + size);
718
while (byteBuffer.position() <= size)
724
logger.finest(getLoggingFilename() + ":" + "looking for next frame at:" + byteBuffer.position());
725
next = new ID3v24Frame(byteBuffer, getLoggingFilename());
726
id = next.getIdentifier();
727
loadFrameIntoMap(id, next);
730
catch (EmptyFrameException ex)
732
logger.warning(getLoggingFilename() + ":" + "Empty Frame:" + ex.getMessage());
733
this.emptyFrameBytes += TAG_HEADER_LENGTH;
735
catch (InvalidFrameIdentifierException ifie)
737
logger.info(getLoggingFilename() + ":" + "Invalid Frame Identifier:" + ifie.getMessage());
738
this.invalidFrameBytes++;
739
//Dont try and find any more frames
742
//Problem trying to find frame
743
catch (InvalidFrameException ife)
745
logger.warning(getLoggingFilename() + ":" + "Invalid Frame:" + ife.getMessage());
746
this.invalidFrameBytes++;
747
//Dont try and find any more frames
754
* Write the ID3 header to the ByteBuffer.
756
* TODO Calculate the CYC Data Check
757
* TODO Reintroduce Extended Header
759
* @param padding is the size of the padding
760
* @param size is the size of the body data
762
* @throws IOException
764
private ByteBuffer writeHeaderToBuffer(int padding, int size) throws IOException
766
//This would only be set if every frame in tag has been unsynchronized, I only unsychronize frames
767
//that need it, in any case I have been advised not to set it even then.
768
unsynchronization = false;
770
// Flags,currently we never calculate the CRC
771
// and if we dont calculate them cant keep orig values. Tags are not
772
// experimental and we never create extended header to keep things simple.
774
experimental = false;
777
// Create Header Buffer,allocate maximum possible size for the header
778
ByteBuffer headerBuffer = ByteBuffer.allocate(TAG_HEADER_LENGTH);
780
headerBuffer.put(TAG_ID);
783
headerBuffer.put(getMajorVersion());
786
headerBuffer.put(getRevision());
790
if (isUnsynchronization() == true)
792
flagsByte |= MASK_V24_UNSYNCHRONIZATION;
794
if (extended == true)
796
flagsByte |= MASK_V24_EXTENDED_HEADER;
798
if (experimental == true)
800
flagsByte |= MASK_V24_EXPERIMENTAL;
804
flagsByte |= MASK_V24_FOOTER_PRESENT;
806
headerBuffer.put(flagsByte);
808
//Size As Recorded in Header, don't include the main header length
809
//Additional Header Size,(for completeness we never actually write the extended header, or footer)
810
int additionalHeaderSize = 0;
813
additionalHeaderSize += this.TAG_EXT_HEADER_LENGTH;
816
additionalHeaderSize += this.TAG_EXT_HEADER_UPDATE_LENGTH;
820
additionalHeaderSize += this.TAG_EXT_HEADER_CRC_LENGTH;
824
additionalHeaderSize += this.TAG_EXT_HEADER_RESTRICTION_LENGTH;
828
//Size As Recorded in Header, don't include the main header length
829
headerBuffer.put(ID3SyncSafeInteger.valueToBuffer(padding + size + additionalHeaderSize));
831
//Write Extended Header
832
ByteBuffer extHeaderBuffer = null;
833
if (extended == true)
835
//Write Extended Header Size
836
int extendedSize = TAG_EXT_HEADER_LENGTH;
837
if (updateTag == true)
839
extendedSize += TAG_EXT_HEADER_UPDATE_LENGTH;
841
if (crcDataFlag == true)
843
extendedSize += TAG_EXT_HEADER_CRC_LENGTH;
845
if (tagRestriction == true)
847
extendedSize += TAG_EXT_HEADER_RESTRICTION_LENGTH;
849
extHeaderBuffer = ByteBuffer.allocate(extendedSize);
850
extHeaderBuffer.putInt(extendedSize);
851
//Write Number of flags Byte
852
extHeaderBuffer.put((byte) TAG_EXT_NUMBER_BYTES_DATA_LENGTH);
853
//Write Extended Flags
857
extFlag |= MASK_V24_TAG_UPDATE;
861
extFlag |= MASK_V24_CRC_DATA_PRESENT;
865
extFlag |= MASK_V24_TAG_RESTRICTIONS;
867
extHeaderBuffer.put(extFlag);
871
extHeaderBuffer.put((byte) 0);
876
extHeaderBuffer.put((byte) TAG_EXT_HEADER_CRC_DATA_LENGTH);
877
extHeaderBuffer.put((byte) 0);
878
extHeaderBuffer.putInt(crcData);
880
//Write Tag Restriction
883
extHeaderBuffer.put((byte) TAG_EXT_HEADER_RESTRICTION_DATA_LENGTH);
884
//todo not currently setting restrictions
885
extHeaderBuffer.put((byte) 0);
889
if (extHeaderBuffer != null)
891
extHeaderBuffer.flip();
892
headerBuffer.put(extHeaderBuffer);
900
* Write this tag to file.
903
* @throws IOException
905
public void write(File file, long audioStartLocation) throws IOException
907
logger.info("Writing tag to file");
910
byte[] bodyByteBuffer = writeFramesToBuffer().toByteArray();
912
//Calculate Tag Size including Padding
913
int sizeIncPadding = calculateTagSize(bodyByteBuffer.length + TAG_HEADER_LENGTH, (int) audioStartLocation);
915
//Calculate padding bytes required
916
int padding = sizeIncPadding - (bodyByteBuffer.length + TAG_HEADER_LENGTH);
918
ByteBuffer headerBuffer = writeHeaderToBuffer(padding, bodyByteBuffer.length);
920
//We need to adjust location of audio File
921
if (sizeIncPadding > audioStartLocation)
923
logger.finest("Adjusting Padding");
924
adjustPadding(file, sizeIncPadding, audioStartLocation);
927
//Write changes to file
928
FileChannel fc = null;
931
fc = new RandomAccessFile(file, "rw").getChannel();
932
fc.write(headerBuffer);
933
fc.write(ByteBuffer.wrap(bodyByteBuffer));
934
fc.write(ByteBuffer.wrap(new byte[padding]));
946
* Write tag to channel
949
* @throws IOException
951
public void write(WritableByteChannel channel) throws IOException
953
logger.info("Writing tag to channel");
955
byte[] bodyByteBuffer = writeFramesToBuffer().toByteArray();
956
ByteBuffer headerBuffer = writeHeaderToBuffer(0, bodyByteBuffer.length);
958
channel.write(headerBuffer);
959
channel.write(ByteBuffer.wrap(bodyByteBuffer));
963
* Display the tag in an XMLFormat
965
public void createStructure()
967
MP3File.getStructureFormatter().openHeadingElement(TYPE_TAG, getIdentifier());
969
super.createStructureHeader();
972
MP3File.getStructureFormatter().openHeadingElement(TYPE_HEADER, "");
973
MP3File.getStructureFormatter().addElement(TYPE_UNSYNCHRONISATION, this.isUnsynchronization());
974
MP3File.getStructureFormatter().addElement(TYPE_CRCDATA, this.crcData);
975
MP3File.getStructureFormatter().addElement(TYPE_EXPERIMENTAL, this.experimental);
976
MP3File.getStructureFormatter().addElement(TYPE_EXTENDED, this.extended);
977
MP3File.getStructureFormatter().addElement(TYPE_PADDINGSIZE, this.paddingSize);
978
MP3File.getStructureFormatter().addElement(TYPE_FOOTER, this.footer);
979
MP3File.getStructureFormatter().addElement(TYPE_IMAGEENCODINGRESTRICTION, this.paddingSize);
980
MP3File.getStructureFormatter().addElement(TYPE_IMAGESIZERESTRICTION, (int) this.imageSizeRestriction);
981
MP3File.getStructureFormatter().addElement(TYPE_TAGRESTRICTION, this.tagRestriction);
982
MP3File.getStructureFormatter().addElement(TYPE_TAGSIZERESTRICTION, (int) this.tagSizeRestriction);
983
MP3File.getStructureFormatter().addElement(TYPE_TEXTFIELDSIZERESTRICTION, (int) this.textFieldSizeRestriction);
984
MP3File.getStructureFormatter().addElement(TYPE_TEXTENCODINGRESTRICTION, (int) this.textEncodingRestriction);
985
MP3File.getStructureFormatter().addElement(TYPE_UPDATETAG, this.updateTag);
986
MP3File.getStructureFormatter().closeHeadingElement(TYPE_HEADER);
989
super.createStructureBody();
991
MP3File.getStructureFormatter().closeHeadingElement(TYPE_TAG);
995
* Are all frame swithin this tag unsynchronized
997
* <p>Because synchronization occurs at the frame level it is not normally desirable to unsynchronize all frames
998
* and hence this flag is not normally set.
1000
* @return are all frames within the tag unsynchronized
1002
public boolean isUnsynchronization()
1004
return unsynchronization;
1007
protected String getArtistId()
1009
return ID3v24Frames.FRAME_ID_ARTIST;
1012
protected String getAlbumId()
1014
return ID3v24Frames.FRAME_ID_ALBUM;
1017
protected String getTitleId()
1019
return ID3v24Frames.FRAME_ID_TITLE;
1022
protected String getTrackId()
1024
return ID3v24Frames.FRAME_ID_TRACK;
1027
protected String getYearId()
1029
return ID3v24Frames.FRAME_ID_YEAR;
1032
protected String getCommentId()
1034
return ID3v24Frames.FRAME_ID_COMMENT;
1037
protected String getGenreId()
1039
return ID3v24Frames.FRAME_ID_GENRE;
1043
* Create a new frame with the specified frameid
1048
public ID3v24Frame createFrame(String id)
1050
return new ID3v24Frame(id);
1056
* Create Frame for Id3 Key
1058
* Only textual data supported at the moment, should only be used with frames that
1059
* support a simple string argument.
1064
* @throws KeyNotFoundException
1065
* @throws FieldDataInvalidException
1067
public TagField createTagField(ID3v24FieldKey id3Key, String value)
1068
throws KeyNotFoundException, FieldDataInvalidException
1072
throw new KeyNotFoundException();
1074
return super.doCreateTagField(new FrameAndSubId(id3Key.getFrameId(),id3Key.getSubId()),value);
1078
* Retrieve the first value that exists for this id3v24key
1080
* @param id3v24FieldKey
1083
public String getFirst(ID3v24FieldKey id3v24FieldKey) throws KeyNotFoundException
1085
if (id3v24FieldKey == null)
1087
throw new KeyNotFoundException();
1089
return super.doGetFirst(new FrameAndSubId(id3v24FieldKey.getFrameId(),id3v24FieldKey.getSubId()));
1095
* Delete fields with this id3v24FieldKey
1097
* @param id3v24FieldKey
1099
public void deleteTagField
1101
id3v24FieldKey) throws KeyNotFoundException
1103
if (id3v24FieldKey == null)
1105
throw new KeyNotFoundException();
1107
super.doDeleteTagField(new FrameAndSubId(id3v24FieldKey.getFrameId(),id3v24FieldKey.getSubId()));
1111
protected FrameAndSubId getFrameAndSubIdFromGenericKey(TagFieldKey genericKey)
1113
ID3v24FieldKey id3v24FieldKey = ID3v24Frames.getInstanceOf().getId3KeyFromGenericKey(genericKey);
1114
if (id3v24FieldKey == null)
1116
throw new KeyNotFoundException();
1118
return new FrameAndSubId(id3v24FieldKey.getFrameId(),id3v24FieldKey.getSubId());
1121
protected ID3Frames getID3Frames()
1123
return ID3v24Frames.getInstanceOf();