在寫netbeans的音樂插件的時候,為了讀取ID3v1標簽,曾經寫了一個ID3v1格式的標簽讀寫器,但是ID3v1格式的擴展性卻不太好,這個時候APEv2格式就很適合了,首先它的編碼是很標準,都是統一UTF-8編碼,不會出現亂碼的問題,其次它的擴展性很好,并不像ID3v1一樣限制128個字節.但是找了很多都沒有找到APEv2的標簽讀寫器,沒辦法,只能自己動手寫了,在寫之前必須了解APEv2格式標簽的文件結構,具體的文件結構可以參見http://wiki.hydrogenaudio.org/index.php?title=APEv2_specification,剛看到這個網頁的時候,看得云里霧里的,看了好久才明白它的結構.
明白了構造以后就要開始編碼了,但是我不能單獨編碼啊,我得把這個類集成到jaudiotagger庫里面去,這個才便于統一管理啊,于是又研究了jaudiotagger的源碼,了解了這個庫的文件組織以及結構以后,才把我新寫的APEv2格式標簽讀寫器插入到jaudiotagger的組織里去,為了管理的方便,我把jaudiotagger集成到了我的源碼里面,一下子源碼就多出來十幾個包,一打開netbeans,再打開工程,一長列的包就看到了,要不要把它加進源碼當時我也有想過,加進來會使得YOYOPlayer工程的源碼很多,很長,但是為了統一管理日志,以及插入自己需要的標簽讀寫器,還有要對源碼做一些修改,最后還是把這個jaudiotagger的源碼加入進來了.
以下是APEv2格式標簽的讀寫代碼,實現了jaudiotagger里面的一些接口以便于管理.
APEv2.java代表一個標簽
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.hadeslee.audiotag.tag.ape;
import com.hadeslee.audiotag.tag.FieldDataInvalidException;
import com.hadeslee.audiotag.tag.KeyNotFoundException;
import com.hadeslee.audiotag.tag.Tag;
import com.hadeslee.audiotag.tag.TagField;
import com.hadeslee.audiotag.tag.TagFieldKey;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.sound.sampled.UnsupportedAudioFileException;
/**
*
* @author hadeslee
*/
public class APEv2Tag implements Tag {
private static Logger log = Logger.getLogger(APEv2Tag.class.getName());
private File input;
private TagHead head;
private TagBody body;
private String artist = "";
private String album = "";
private String title = "";
private String year = "";
private String comment = "";
private String track = "";
private String genre = "";
private int fieldCount;
private Map<String, String> map;
public APEv2Tag(File file) throws IOException, UnsupportedAudioFileException {
this.input = file;
map = new HashMap<String, String>();
load();
}
public APEv2Tag() {
map = new HashMap<String, String>();
}
protected void load() throws IOException, UnsupportedAudioFileException {
RandomAccessFile raf = new RandomAccessFile(input, "r");
//先查看最后32個字節
try {
raf.seek((int) (input.length() - 32));
byte[] buffer = new byte[32];
raf.read(buffer);
head = new TagHead(buffer);
if (head.isValid()) {
log.log(Level.INFO, "讀取:最后32個字節有標簽!");
int size = head.getTagSize();
raf.seek((int) (input.length() - size));
buffer = new byte[size - 32];
int read = 0;
while (read < buffer.length) {
read += raf.read(buffer, read, buffer.length - read);
}
body = new TagBody(buffer);
List<TagItem> list = body.getItems();
for (TagItem item : list) {
log.log(Level.INFO, item.toString());
}
} else {//再查看128前面的32個字節
raf.seek((int) (input.length() - 32 - 128));
raf.read(buffer);
head = new TagHead(buffer);
if (head.isValid()) {
log.log(Level.INFO, "讀取:ID3v1前面的字節有標簽!");
int size = head.getTagSize();
raf.seek((int) (input.length() - size - 128));
buffer = new byte[size - 32];
int read = 0;
while (read < buffer.length) {
read += raf.read(buffer, read, buffer.length - read);
}
body = new TagBody(buffer);
List<TagItem> list = body.getItems();
for (TagItem item : list) {
log.log(Level.INFO, item.toString());
}
} else {
throw new UnsupportedAudioFileException("讀取:找不到APEv2格式的標簽!");
}
}
} finally {
try {
raf.close();
readTag();
} catch (Exception exe) {
throw new UnsupportedAudioFileException("讀取:找不到APEv2格式的標簽!");
}
}
}
private void readTag() {
for (TagItem item : body.getItems()) {
map.put(item.getId(), item.getContent());
}
this.album = map.get(APEv2FieldKey.Album.name());
this.artist = map.get(APEv2FieldKey.Artist.name());
this.comment = map.get(APEv2FieldKey.Comment.name());
this.genre = map.get(APEv2FieldKey.Genre.name());
this.title = map.get(APEv2FieldKey.Title.name());
this.track = map.get(APEv2FieldKey.Track.name());
this.year = map.get(APEv2FieldKey.Year.name());
}
protected List<TagField> returnFieldToList(TagItem field) {
List<TagField> fields = new ArrayList<TagField>();
fields.add(field);
return fields;
}
/**
* 寫出APE標簽到文件里面去
* @param raf 隨機文件流
* @param hasID3v1 是否有ID3v1標簽
* @throws java.io.IOException
*/
public void write(RandomAccessFile raf, boolean hasID3v1) throws IOException {
//如果有ID3標簽,則先把它緩存起來,總共128個字節
byte[] temp = null;
int deleteLength = 0;
if (hasID3v1) {
temp = new byte[128];
raf.seek(raf.length() - 128);
raf.read(temp);
deleteLength += 128;
}
TagHead header = checkTag(raf);
//如果有標頭,則說明有APE的標簽,還要多刪一些
if (header != null) {
log.log(Level.INFO, "原來存在APEv2標簽,先刪除之
");
int length = header.getTagSize();
if (header.hasHeader()) {//如果有標頭的話,長度還要加32個字節
length += 32;
}
deleteLength += length;
} else {
log.log(Level.INFO, "以前不存在APEv2標簽,直接添加
");
}
raf.setLength(raf.length() - deleteLength);
//把該截掉的都截了以后,就開始寫標簽了,先寫APE的,再看
//有沒有ID3的,有就寫,沒有就不寫了
raf.seek(raf.length());
byte[] data = getTagBytes();
raf.write(data);
if (temp != null) {
raf.write(temp);
}
log.log(Level.INFO, "APEv2標簽寫出完畢
");
}
/**
* 得到標簽所代表的字節數組
* @return 標簽所代表的字節數組
*/
private byte[] getTagBytes() throws UnsupportedEncodingException, IOException {
int itemCount = map.size();
body = new TagBody();
for (Map.Entry<String, String> en : map.entrySet()) {
body.addTagItem(new TagItem(en.getKey(), en.getValue()));
}
byte[] bodyData = body.getBytes();
log.log(Level.SEVERE, "BODYSIZE=" + bodyData.length);
TagHead header = new TagHead();
header.setFlag(TagHead.HEAD);
header.setItemCount(itemCount);
header.setTagSize(bodyData.length + 32);
header.setVersion(TagHead.V2);
TagHead foot = new TagHead();
foot.setFlag(TagHead.FOOT);
foot.setItemCount(itemCount);
foot.setTagSize(bodyData.length + 32);
foot.setVersion(TagHead.V2);
ByteArrayOutputStream bout = new ByteArrayOutputStream();
bout.write(header.getBytes());
bout.write(bodyData);
bout.write(foot.getBytes());
bout.flush();
return bout.toByteArray();
}
/**
* 檢查是否已經存在APE的標簽了,主要查兩個地方
* 一個是最后的字節,還有一個是最后128字節以上的字節
* 因為最后的字節可能寫入了ID3v1標簽
* @param raf 文件
* @return 得到標簽頭
* @throws java.io.IOException
*/
private TagHead checkTag(RandomAccessFile raf) throws IOException {
raf.seek((int) (raf.length() - 32));
byte[] buffer = new byte[32];
raf.read(buffer);
TagHead header = new TagHead(buffer);
if (header.isValid()) {
header.setIndex(0);
return header;
} else {
raf.seek((int) (raf.length() - 32 - 128));
raf.read(buffer);
header = new TagHead(buffer);
if (header.isValid()) {
header.setIndex(128);
return header;
} else {
return null;
}
}
}
/**
* 刪除標簽,如果存在ID3v1的話,就要先保存它然后刪除后面部份
* 把它寫回來
* @param raf 寫出文件
* @param hasID3v1 是否有ID3v1標簽
* @throws java.io.IOException
*/
public void delete(RandomAccessFile raf, boolean hasID3v1) throws IOException {
//如果有ID3標簽,則先把它緩存起來,總共128個字節
byte[] temp = null;
int deleteLength = 0;
if (hasID3v1) {
temp = new byte[128];
raf.seek(raf.length() - 128);
raf.read(temp);
deleteLength += 128;
}
TagHead header = checkTag(raf);
//如果有標頭,則說明有APE的標簽,還要多刪一些
if (header != null) {
log.log(Level.INFO, "原來存在APEv2標簽,先刪除之
");
int length = header.getTagSize();
if (header.hasHeader()) {//如果有標頭的話,長度還要加32個字節
length += 32;
}
deleteLength += length;
}
raf.setLength(raf.length() - deleteLength);
log.log(Level.INFO, "APEv2標簽刪除完畢
");
}
public void add(TagField field) throws FieldDataInvalidException {
}
public void addAlbum(String album) throws FieldDataInvalidException {
setAlbum(album);
}
public void addArtist(String artist) throws FieldDataInvalidException {
setArtist(artist);
}
public void addComment(String comment) throws FieldDataInvalidException {
setComment(comment);
}
public void addGenre(String genre) throws FieldDataInvalidException {
setGenre(genre);
}
public void addTitle(String title) throws FieldDataInvalidException {
setTitle(title);
}
public void addTrack(String track) throws FieldDataInvalidException {
setTrack(track);
}
public void addYear(String year) throws FieldDataInvalidException {
setYear(year);
}
public List<TagField> get(String id) {
return null;
}
public List<TagField> getAlbum() {
if (getFirstAlbum().length() > 0) {
TagItem field = new TagItem(APEv2FieldKey.Album.name(), getFirstAlbum());
return returnFieldToList(field);
} else {
return new ArrayList<TagField>();
}
}
public List<TagField> getArtist() {
if (getFirstAlbum().length() > 0) {
TagItem field = new TagItem(APEv2FieldKey.Artist.name(), getFirstArtist());
return returnFieldToList(field);
} else {
return new ArrayList<TagField>();
}
}
public List<TagField> getComment() {
if (getFirstAlbum().length() > 0) {
TagItem field = new TagItem(APEv2FieldKey.Comment.name(), getFirstComment());
return returnFieldToList(field);
} else {
return new ArrayList<TagField>();
}
}
public List<TagField> getGenre() {
if (getFirstAlbum().length() > 0) {
TagItem field = new TagItem(APEv2FieldKey.Genre.name(), getFirstGenre());
return returnFieldToList(field);
} else {
return new ArrayList<TagField>();
}
}
public List<TagField> getTitle() {
if (getFirstAlbum().length() > 0) {
TagItem field = new TagItem(APEv2FieldKey.Title.name(), getFirstTitle());
return returnFieldToList(field);
} else {
return new ArrayList<TagField>();
}
}
public List<TagField> getTrack() {
if (getFirstAlbum().length() > 0) {
TagItem field = new TagItem(APEv2FieldKey.Track.name(), getFirstTrack());
return returnFieldToList(field);
} else {
return new ArrayList<TagField>();
}
}
public List<TagField> getYear() {
if (getFirstAlbum().length() > 0) {
TagItem field = new TagItem(APEv2FieldKey.Year.name(), getFirstYear());
return returnFieldToList(field);
} else {
return new ArrayList<TagField>();
}
}
public String getFirstAlbum() {
return this.album;
}
public String getFirstArtist() {
return artist;
}
public String getFirstComment() {
return comment;
}
public String getFirstGenre() {
return genre;
}
public String getFirstTitle() {
return title;
}
public String getFirstTrack() {
return track;
}
public String getFirstYear() {
return year;
}
public boolean hasCommonFields() {
return true;
}
public boolean hasField(String id) {
throw new UnsupportedOperationException("Not supported yet.");
}
public boolean isEmpty() {
throw new UnsupportedOperationException("Not supported yet.");
}
public void set(TagField field) throws FieldDataInvalidException {
TagFieldKey genericKey = TagFieldKey.valueOf(field.getId());
switch (genericKey) {
case ARTIST:
setArtist(field.toString());
break;
case ALBUM:
setAlbum(field.toString());
break;
case TITLE:
setTitle(field.toString());
break;
case GENRE:
setGenre(field.toString());
break;
case YEAR:
setYear(field.toString());
break;
case COMMENT:
setComment(field.toString());
break;
}
}
public void setAlbum(String s) throws FieldDataInvalidException {
this.album = s;
map.put(APEv2FieldKey.Album.name(), album);
}
public void setArtist(String s) throws FieldDataInvalidException {
this.artist = s;
map.put(APEv2FieldKey.Artist.name(), s);
}
public void setComment(String s) throws FieldDataInvalidException {
this.comment = s;
map.put(APEv2FieldKey.Comment.name(), s);
}
public void setGenre(String s) throws FieldDataInvalidException {
this.genre = s;
map.put(APEv2FieldKey.Genre.name(), s);
}
public void setTitle(String s) throws FieldDataInvalidException {
this.title = s;
map.put(APEv2FieldKey.Title.name(), s);
}
public void setTrack(String s) throws FieldDataInvalidException {
this.track = s;
map.put(APEv2FieldKey.Track.name(), s);
}
public void setYear(String s) throws FieldDataInvalidException {
this.year = s;
map.put(APEv2FieldKey.Year.name(), s);
}
public TagField createTagField(TagFieldKey genericKey, String value) throws KeyNotFoundException, FieldDataInvalidException {
throw new UnsupportedOperationException("Not supported yet.");
}
public String getFirst(String id) {
throw new UnsupportedOperationException("Not supported yet.");
}
public String getFirst(TagFieldKey id) throws KeyNotFoundException {
throw new UnsupportedOperationException("Not supported yet.");
}
public TagField getFirstField(String id) {
throw new UnsupportedOperationException("Not supported yet.");
}
public void deleteTagField(TagFieldKey tagFieldKey) throws KeyNotFoundException {
throw new UnsupportedOperationException("Not supported yet.");
}
public Iterator getFields() {
throw new UnsupportedOperationException("Not supported yet.");
}
public int getFieldCount() {
throw new UnsupportedOperationException("Not supported yet.");
}
public boolean setEncoding(String enc) throws FieldDataInvalidException {
return false;
}
public List<TagField> get(TagFieldKey id) throws KeyNotFoundException {
throw new UnsupportedOperationException("Not supported yet.");
}
public static void main(String[] args) throws Exception {
System.out.println(0xD2);
// APEv2Tag tag = new APEv2Tag(new File("D:\\難道愛一個人有錯嗎.mp3"));
// tag.load();
// System.out.println("tag.album:" + tag.getFirstAlbum());
// System.out.println("tag.title:" + tag.getFirstTitle());
// System.out.println("tag.artist:" + tag.getFirstArtist());
}
}
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.hadeslee.audiotag.tag.ape;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
/**
* 內部私有類,它代表了一個APE標簽的內容
* @author hadeslee
*/
public class TagBody {
private byte[] data;//標簽的數據
private List<TagItem> items;//所有的項
public TagBody(byte[] data) {
this.data = data;
items = new ArrayList<TagItem>();
parseData();
}
public TagBody() {
items = new ArrayList<TagItem>();
}
public List<TagItem> getItems() {
return items;
}
public byte[] getBytes() throws UnsupportedEncodingException, IOException {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
for(TagItem item:items){
bout.write(item.getRawContent());
}
bout.flush();
return bout.toByteArray();
}
public void addTagItem(TagItem item) {
items.add(item);
}
private void parseData() {
int count = 0;
byte[] temp = new byte[data.length];
System.arraycopy(data, 0, temp, 0, data.length);
while (count < data.length) {
TagItem item = new TagItem(temp, count);
if (item.isValid()) {
count += item.getSize();
items.add(item);
} else {
return;
}
}
}
}
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.hadeslee.audiotag.tag.ape;
import com.hadeslee.yoyoplayer.util.Util;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* 內部的私有類,它代表了一個APE標簽的頭部
* @author hadeslee
*/
public class TagHead {
private static Logger log = Logger.getLogger(TagHead.class.getName());
private byte[] data;//頭部的數據
private boolean valid;//是否是合法的頭部
private int version = 2000;//版本,默認是2000
private int tagSize;//標簽的長度,包括尾標簽以及所有的項目,不包括頭標簽
private int itemCount;//項目的數量
private int flag = FOOT;//標簽的其它標志,指示它是頭部還是尾部
public static final int HEAD = 0xA0000000;
public static final int FOOT = 0x80000000;
public static final int V1=1000;//表示APE的版本號
public static final int V2=2000;
private int index;//這個標頭的起始點的位置,相對于文件
public TagHead(byte[] data) {
this.data = data;
parseData();
}
public TagHead() {
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
public byte[] getBytes() {
//標頭總共是32個字節
byte[] head = new byte[32];
byte[] temp = {(byte) 'A',
(byte) 'P',
(byte) 'E',
(byte) 'T',
(byte) 'A',
(byte) 'G',
(byte) 'E',
(byte) 'X'
};
//先把頭的標量填進去,8字節
System.arraycopy(temp, 0, head, 0, 8);
temp = Util.getBytesFromInt(version);
//再把版本號寫進去,4字節
System.arraycopy(temp, 0, head, 8, 4);
temp = Util.getBytesFromInt(tagSize);
log.log(Level.SEVERE,"TAGSIZE="+tagSize);
//再把標簽的長度寫進去,4字節
System.arraycopy(temp, 0, head, 12, 4);
temp = Util.getBytesFromInt(itemCount);
//再把標簽的數量寫進去,4字節
System.arraycopy(temp, 0, head, 16, 4);
temp = Util.getBytesFromInt(flag);
//再把標志寫進去,表示是標簽頭部還是尾部
System.arraycopy(temp, 0, head, 20, 4);
//把標志空的8個字節進去,因為默認就是空的,所以不用寫了
//頭部或者尾部的數據塊已經構造好了
return head;
}
private void parseData() {
try {
checkHead();
checkVersion();
checkTagSize();
checkItemCount();
checkFlag();
valid = true;
} catch (Exception exe) {
log.log(Level.SEVERE, "分析標簽異常!");
valid = false;
}
}
/**
* 這個標簽是否有頭標簽,因為一般讀都是從尾部讀過去的
* @return 是否有頭標簽,重寫的時候有用
*/
public boolean hasHeader() {
return ((1 << 31) & flag) != 0;
}
/**
* 檢查頭部八個字節的的數據是否一樣
*/
private void checkHead() {
byte[] temp = new byte[8];
byte[] head = {(byte) 'A',
(byte) 'P',
(byte) 'E',
(byte) 'T',
(byte) 'A',
(byte) 'G',
(byte) 'E',
(byte) 'X'
};
System.arraycopy(data, 0, temp, 0, 8);
//比較兩個頭部的數據是否一樣,這是第一要素
if (!Arrays.equals(head, temp)) {
throw new RuntimeException("頭部數據不一樣!");
}
}
/**
* 檢查版本號是否合法,必須是1000或者2000
*/
private void checkVersion() {
byte[] temp = new byte[4];
System.arraycopy(data, 8, temp, 0, 4);
int v = Util.getInt(temp);
if (v == 2000 || v == 1000) {
version = v;
log.log(Level.INFO, "版本號是:" + v);
} else {
throw new RuntimeException("版本號不合法!!");
}
}
private void checkTagSize() {
byte[] temp = new byte[4];
System.arraycopy(data, 12, temp, 0, 4);
tagSize = Util.getInt(temp);
log.log(Level.INFO, "標簽大小:" + tagSize);
}
private void checkItemCount() {
byte[] temp = new byte[4];
System.arraycopy(data, 16, temp, 0, 4);
itemCount = Util.getInt(temp);
log.log(Level.INFO, "標簽項目數:" + itemCount);
}
private void checkFlag() {
byte[] temp = new byte[4];
System.arraycopy(data, 20, temp, 0, 4);
flag = Util.getInt(temp);
log.log(Level.INFO, "標志:" + flag);
}
public void setFlag(int flag) {
this.flag = flag;
}
public void setItemCount(int itemCount) {
this.itemCount = itemCount;
}
public void setTagSize(int tagSize) {
this.tagSize = tagSize;
}
public void setVersion(int version) {
if(!(version==V1||version==V2)){
throw new RuntimeException("非法的版本號,只能是V2或者V1.");
}
this.version = version;
}
public int getFlag() {
return flag;
}
public int getItemCount() {
return itemCount;
}
public int getTagSize() {
return tagSize;
}
public boolean isValid() {
return valid;
}
public int getVersion() {
return version;
}
public static void main(String[] args) {
}
}
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.hadeslee.audiotag.tag.ape;
import com.hadeslee.audiotag.tag.TagField;
import com.hadeslee.audiotag.tag.TagTextField;
import com.hadeslee.yoyoplayer.util.Util;
import java.io.UnsupportedEncodingException;
import java.util.logging.Logger;
/**
*
* @author hadeslee
*/
public class TagItem implements TagTextField {
private static Logger log = Logger.getLogger(TagItem.class.getName());
private boolean common;
private String id;
private String content;
private boolean valid;//是否合法
private int length;//該項的內容的長度
private int flag;//該項的標志,表明內容是什么,可能是UTF-8字符串也可能是二進制數據
private int size;//這個項用了多少個字節
private byte[] raw;
public TagItem(String id, String content) {
this.id = id;
this.content = content;
valid = true;
checkCommon();
}
public TagItem(byte[] raw, int offset) {
parseData(raw, offset);
}
public boolean isValid() {
return valid;
}
public int getSize() {
return size;
}
private void parseData(byte[] data, int offset) {
try {
byte[] temp = new byte[4];
System.arraycopy(data, offset, temp, 0, 4);
length = Util.getInt(temp);
System.arraycopy(data, offset + 4, temp, 0, 4);
flag = Util.getInt(temp);
int count = 0;
size += 8;
size += length;
for (int i = 8 + offset; i < data.length; i++) {
// if(data[i]>=0x20&&data[i]<=0x7E){}
//只要數據不是0,就一直到后面去
if (data[i] == 0x00) {
break;
} else {
count++;
}
}
id = new String(data, 8 + offset, count, "UTF-8");
//加上一個空白
count++;
size += count;
content = new String(data, 8 + count + offset, length, "UTF-8");
valid = true;
checkCommon();
} catch (Exception ex) {
valid = false;
}
}
private void checkCommon() {
this.common = id.equals(APEv2FieldKey.Title.name()) ||
id.equals(APEv2FieldKey.Album.name()) ||
id.equals(APEv2FieldKey.Artist.name()) ||
id.equals(APEv2FieldKey.Genre.name()) ||
id.equals(APEv2FieldKey.Year.name()) ||
id.equals(APEv2FieldKey.Comment.name()) ||
id.equals(APEv2FieldKey.Track.name());
}
public String getContent() {
return this.content;
}
public String getEncoding() {
return "UTF-8";
}
public void setContent(String content) {
this.content = content;
}
public void setEncoding(String encoding) {
//什么都不做,因為APE的標簽必須是UTF-8編碼
}
public void copyContent(TagField field) {
if (field instanceof TagTextField) {
this.content = ((TagTextField) field).getContent();
}
}
public String getId() {
return id;
}
public byte[] getRawContent() throws UnsupportedEncodingException {
int index = 0;
byte[] idData = id.getBytes("UTF-8");
byte[] contentData = content.getBytes("UTF-8");
raw = new byte[9 + idData.length + contentData.length];
byte[] temp = Util.getBytesFromInt(contentData.length);
//本項目數據部份的長度,4字節
System.arraycopy(temp, 0, raw, index, 4);
index += 4;
temp = new byte[4];
//中間4個字節留空
System.arraycopy(temp, 0, raw, index, 4);
index += 4;
//項目的鍵值的字節數組
System.arraycopy(idData, 0, raw, index, idData.length);
index += idData.length;
//一個固定的空白分隔符,跳過一個字節,因為默認就是空白的
index += 1;
//項目的內容
System.arraycopy(contentData, 0, raw, index, contentData.length);
return raw;
}
public boolean isBinary() {
return false;
}
public void isBinary(boolean b) {
}
public boolean isCommon() {
return common;
}
public boolean isEmpty() {
return content == null || content.equals("");
}
public String toString() {
return id + ":" + content;
}
}
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.hadeslee.audiotag.tag.ape;
/**
*
* @author hadeslee
*/
public enum APEv2FieldKey {
Artist,
Album,
Genre,
Title,
Year,
Track,
Comment
}
以上便是APEv2格式標簽的讀寫類,并且在jaudiotagger里面的MP3File類里面做了相應的修改,以期能統一管理這些標簽
盡管千里冰封
依然擁有晴空
你我共同品味JAVA的濃香.
posted on 2008-01-09 23:42
千里冰封 閱讀(5831)
評論(5) 編輯 收藏 所屬分類:
JAVASE