YOYOPlayer开发手记(三)APEv2标签读写

来源(千里冰封 - BlogJava)

From: http://www.blogjava.net/hadeslee/archive/2008/02/11/173488.html

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 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类里面做了相应的修改,以期能统一管理这些标签



Link: http://www.asm32.net/article_details.aspx?id=4582


浏览次数 26 发布时间 2009/9/26 14:18:56 从属分类 Java 【评论】【 】【打印】【关闭
 
| www.asm32.net | 2006版 | 资料中心 | linux | asm/asm32 | C/C++ | VC++ | java | Python | 书签 | ASP.Net书签 | 京ICP备09029108号-1