YOYOPlayer开发手记(三)APEv2标签读写
在写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 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 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.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;
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
}
* 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的浓香.