📄 id3v23tag.java
字号:
/*
* MusicTag Copyright (C)2003,2004
*
* This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser
* General Public License as published by the Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this library; if not,
* you can get a copy from http://www.opensource.org/licenses/lgpl-license.php or write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.hadeslee.audiotag.tag.id3;
import com.hadeslee.audiotag.FileConstants;
import com.hadeslee.audiotag.audio.mp3.MP3File;
import com.hadeslee.audiotag.tag.EmptyFrameException;
import com.hadeslee.audiotag.tag.FieldDataInvalidException;
import com.hadeslee.audiotag.tag.InvalidFrameException;
import com.hadeslee.audiotag.tag.InvalidFrameIdentifierException;
import com.hadeslee.audiotag.tag.InvalidTagException;
import com.hadeslee.audiotag.tag.KeyNotFoundException;
import com.hadeslee.audiotag.tag.TagException;
import com.hadeslee.audiotag.tag.TagField;
import com.hadeslee.audiotag.tag.TagFieldKey;
import com.hadeslee.audiotag.tag.TagNotFoundException;
import com.hadeslee.audiotag.tag.TagOptionSingleton;
import com.hadeslee.audiotag.tag.id3.framebody.FrameBodyTDAT;
import com.hadeslee.audiotag.tag.id3.framebody.FrameBodyTDRC;
import com.hadeslee.audiotag.tag.id3.framebody.FrameBodyTIME;
import com.hadeslee.audiotag.tag.id3.framebody.FrameBodyTYER;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.WritableByteChannel;
import java.util.*;
import java.util.logging.Level;
/**
* Represents an ID3v2.3 tag.
*
* @author : Paul Taylor
* @author : Eric Farng
* @version $Id: ID3v23Tag.java,v 1.31 2007/11/13 14:24:31 paultaylor Exp $
*/
public class ID3v23Tag
extends AbstractID3v2Tag
{
protected static final String TYPE_CRCDATA = "crcdata";
protected static final String TYPE_EXPERIMENTAL = "experimental";
protected static final String TYPE_EXTENDED = "extended";
protected static final String TYPE_PADDINGSIZE = "paddingsize";
protected static final String TYPE_UNSYNCHRONISATION = "unsyncronisation";
protected static int TAG_EXT_HEADER_LENGTH = 10;
protected static int TAG_EXT_HEADER_CRC_LENGTH = 4;
protected static int FIELD_TAG_EXT_SIZE_LENGTH = 4;
protected static int TAG_EXT_HEADER_DATA_LENGTH = TAG_EXT_HEADER_LENGTH - FIELD_TAG_EXT_SIZE_LENGTH;
/**
* ID3v2.3 Header bit mask
*/
public static final int MASK_V23_UNSYNCHRONIZATION = FileConstants.BIT7;
/**
* ID3v2.3 Header bit mask
*/
public static final int MASK_V23_EXTENDED_HEADER = FileConstants.BIT6;
/**
* ID3v2.3 Header bit mask
*/
public static final int MASK_V23_EXPERIMENTAL = FileConstants.BIT5;
/**
* ID3v2.3 Extended Header bit mask
*/
public static final int MASK_V23_CRC_DATA_PRESENT = FileConstants.BIT7;
/**
* ID3v2.3 RBUF frame bit mask
*/
public static final int MASK_V23_EMBEDDED_INFO_FLAG = FileConstants.BIT1;
/**
* CRC Checksum calculated
*/
protected boolean crcDataFlag = false;
/**
* Experiemntal tag
*/
protected boolean experimental = false;
/**
* Contains extended header
*/
protected boolean extended = false;
/**
* CRC Checksum
*/
protected int crcData = 0;
/**
* Tag padding
*/
protected int paddingSize = 0;
/**
* All frames in the tag uses unsynchronisation
*/
protected boolean unsynchronization = false;
/**
* The tag is compressed
*/
protected boolean compression = false;
public static final byte RELEASE = 2;
public static final byte MAJOR_VERSION = 3;
public static final byte REVISION = 0;
/**
* Retrieve the Release
*/
public byte getRelease()
{
return RELEASE;
}
/**
* Retrieve the Major Version
*/
public byte getMajorVersion()
{
return MAJOR_VERSION;
}
/**
* Retrieve the Revision
*/
public byte getRevision()
{
return REVISION;
}
/**
* Creates a new empty ID3v2_3 datatype.
*/
public ID3v23Tag()
{
frameMap = new LinkedHashMap();
}
/**
* Copy primitives applicable to v2.3
*/
protected void copyPrimitives(AbstractID3v2Tag copyObj)
{
logger.info("Copying primitives");
super.copyPrimitives(copyObj);
if (copyObj instanceof ID3v23Tag)
{
ID3v23Tag copyObject = (ID3v23Tag) copyObj;
this.crcDataFlag = copyObject.crcDataFlag;
this.experimental = copyObject.experimental;
this.extended = copyObject.extended;
this.crcData = copyObject.crcData;
this.paddingSize = copyObject.paddingSize;
}
}
/**
* Copy frames from one tag into a v2.3 tag
*
* @param copyObject
*/
protected void copyFrames(AbstractID3v2Tag copyObject)
{
logger.info("Copying Frames,there are:" + copyObject.frameMap.keySet().size() + " different types");
frameMap = new LinkedHashMap();
//Copy Frames that are a valid 2.3 type
Iterator iterator = copyObject.frameMap.keySet().iterator();
AbstractID3v2Frame frame;
ID3v23Frame newFrame = null;
while (iterator.hasNext())
{
String id = (String) iterator.next();
Object o = copyObject.frameMap.get(id);
if (o instanceof AbstractID3v2Frame)
{
frame = (AbstractID3v2Frame) o;
logger.info("Frame is:"+frame.getIdentifier());
//Special case v24 tdrc may need converting to multiple frames, only convert when
//it is a valid tdrc to cope with tdrc being added illegally to a v23 tag which is then
//converted to v24 tag and back again.
if (
(frame.getIdentifier().equals(ID3v24Frames.FRAME_ID_YEAR))
&&
(frame.getBody() instanceof FrameBodyTDRC)
)
{
translateFrame(frame);
}
//Usual Case
else
{
try
{
newFrame = new ID3v23Frame(frame);
logger.info("Adding Frame:"+newFrame.getIdentifier());
frameMap.put(newFrame.getIdentifier(), newFrame);
}
catch(InvalidFrameException ife)
{
logger.log(Level.SEVERE,"Unable to convert frame:"+frame.getIdentifier());
}
}
}
//Multi Frames
else if (o instanceof ArrayList)
{
ArrayList multiFrame = new ArrayList();
for (ListIterator li = ((ArrayList) o).listIterator(); li.hasNext();)
{
frame = (AbstractID3v2Frame) li.next();
logger.info("Frame is MultiFrame:"+frame.getIdentifier());
try
{
newFrame = new ID3v23Frame(frame);
multiFrame.add(newFrame);
}
catch(InvalidFrameException ife)
{
logger.log(Level.SEVERE,"Unable to convert frame:"+frame.getIdentifier(),ife);
}
}
if (newFrame != null)
{
logger.info("Adding MultiFrame:"+newFrame.getIdentifier());
frameMap.put(newFrame.getIdentifier(), multiFrame);
}
}
}
}
/**
* This is used when we need to translate a single frame into multiple frames,
* currently required for v24 TDRC frames.
*/
protected void translateFrame(AbstractID3v2Frame frame)
{
FrameBodyTDRC tmpBody = (FrameBodyTDRC) frame.getBody();
ID3v23Frame newFrame;
if (!tmpBody.getYear().equals(""))
{
newFrame = new ID3v23Frame(ID3v23Frames.FRAME_ID_V3_TYER);
((FrameBodyTYER) newFrame.getBody()).setText(tmpBody.getYear());
logger.info("Adding Frame:"+newFrame.getIdentifier());
frameMap.put(newFrame.getIdentifier(), newFrame);
}
if (!tmpBody.getDate().equals(""))
{
newFrame = new ID3v23Frame(ID3v23Frames.FRAME_ID_V3_TDAT);
((FrameBodyTDAT) newFrame.getBody()).setText(tmpBody.getDate());
logger.info("Adding Frame:"+newFrame.getIdentifier());
frameMap.put(newFrame.getIdentifier(), newFrame);
}
if (!tmpBody.getTime().equals(""))
{
newFrame = new ID3v23Frame(ID3v23Frames.FRAME_ID_V3_TIME);
((FrameBodyTIME) newFrame.getBody()).setText(tmpBody.getTime());
logger.info("Adding Frame:"+newFrame.getIdentifier());
frameMap.put(newFrame.getIdentifier(), newFrame);
}
}
/**
* Copy Constructor, creates a new ID3v2_3 Tag based on another ID3v2_3 Tag
*/
public ID3v23Tag(ID3v23Tag copyObject)
{
//This doesnt do anything.
super(copyObject);
logger.info("Creating tag from another tag of same type");
copyPrimitives(copyObject);
copyFrames(copyObject);
}
/**
* Constructs a new tag based upon another tag of different version/type
*/
public ID3v23Tag(AbstractTag mp3tag)
{
logger.info("Creating tag from a tag of a different version");
frameMap = new LinkedHashMap();
if (mp3tag != null)
{
ID3v24Tag convertedTag;
//Should use simpler copy constructor
if ((mp3tag instanceof ID3v24Tag == false) && (mp3tag instanceof ID3v23Tag == true))
{
throw new UnsupportedOperationException("Copy Constructor not called. Please type cast the argument");
}
if (mp3tag instanceof ID3v24Tag)
{
convertedTag = (ID3v24Tag) mp3tag;
}
//All tags types can be converted to v2.4 so do this to simplify things
else
{
convertedTag = new ID3v24Tag(mp3tag);
}
//Copy Primitives
copyPrimitives(convertedTag);
//Copy Frames
copyFrames(convertedTag);
logger.info("Created tag from a tag of a different version");
}
}
/**
* Creates a new ID3v2_3 datatype.
*
* @param buffer
* @param loggingFilename
* @throws TagException
*/
public ID3v23Tag(ByteBuffer buffer,String loggingFilename)
throws TagException
{
setLoggingFilename(loggingFilename);
this.read(buffer);
}
/**
* Creates a new ID3v2_3 datatype.
*
* @param buffer
* @throws TagException
*
* @deprecated use {@link #ID3v23Tag(ByteBuffer,String)} instead
*/
public ID3v23Tag(ByteBuffer buffer)
throws TagException
{
this(buffer,"");
}
/**
*
*
* @return textual tag identifier
*/
public String getIdentifier()
{
return "ID3v2.30";
}
/**
* Return frame size based upon the sizes of the tags rather than the physical
* no of bytes between start of ID3Tag and start of Audio Data.
*
* TODO this is incorrect, because of subclasses
*
* @return size of tag
*/
public int getSize()
{
int size = TAG_HEADER_LENGTH;
if (extended)
{
size += this.TAG_EXT_HEADER_LENGTH;
if (crcDataFlag)
{
size += this.TAG_EXT_HEADER_CRC_LENGTH;
}
}
size += super.getSize();
return size;
}
/**
* Is Tag Equivalent to another tag
*
* @param obj
* @return true if tag is equivalent to another
*/
public boolean equals(Object obj)
{
if ((obj instanceof ID3v23Tag) == false)
{
return false;
}
ID3v23Tag object = (ID3v23Tag) obj;
if (this.crcData != object.crcData)
{
return false;
}
if (this.crcDataFlag != object.crcDataFlag)
{
return false;
}
if (this.experimental != object.experimental)
{
return false;
}
if (this.extended != object.extended)
{
return false;
}
if (this.paddingSize != object.paddingSize)
{
return false;
}
return super.equals(obj);
}
/**
* Read tag from File
*
* @param buffer The buffer to read the ID3v23 Tag from
*
*/
public void read(ByteBuffer buffer)
throws TagException
{
int size;
if (seek(buffer) == false)
{
throw new TagNotFoundException(getIdentifier() + " tag not found");
}
logger.info(getLoggingFilename()+":"+"Reading tag");
//Flags
byte flags = buffer.get();
unsynchronization = (flags & MASK_V23_UNSYNCHRONIZATION) != 0;
extended = (flags & MASK_V23_EXTENDED_HEADER) != 0;
experimental = (flags & MASK_V23_EXPERIMENTAL) != 0;
if(isUnsynchronization())
{
logger.warning(getLoggingFilename()+":"+"ID3v23 Tag is unsynchronized");
}
if(extended)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -