<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    千里冰封
    JAVA 濃香四溢
    posts - 151,comments - 2801,trackbacks - 0

    在寫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, 08);
            temp 
    = Util.getBytesFromInt(version);
            
    //再把版本號寫進去,4字節
            System.arraycopy(temp, 0, head, 84);
            temp 
    = Util.getBytesFromInt(tagSize);
            log.log(Level.SEVERE,
    "TAGSIZE="+tagSize);
            
    //再把標簽的長度寫進去,4字節
            System.arraycopy(temp, 0, head, 124);
            temp 
    = Util.getBytesFromInt(itemCount);
            
    //再把標簽的數量寫進去,4字節
            System.arraycopy(temp, 0, head, 164);
            temp 
    = Util.getBytesFromInt(flag);
            
    //再把標志寫進去,表示是標簽頭部還是尾部
            System.arraycopy(temp, 0, head, 204);
            
    //把標志空的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, 08);
            
    //比較兩個頭部的數據是否一樣,這是第一要素
            if (!Arrays.equals(head, temp)) {
                
    throw new RuntimeException("頭部數據不一樣!");
            }
        }

        
    /**
         * 檢查版本號是否合法,必須是1000或者2000
         
    */
        
    private void checkVersion() {
            
    byte[] temp = new byte[4];
            System.arraycopy(data, 
    8, temp, 04);
            
    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, 04);
            tagSize 
    = Util.getInt(temp);
            log.log(Level.INFO, 
    "標簽大小:" + tagSize);
        }

        
    private void checkItemCount() {
            
    byte[] temp = new byte[4];
            System.arraycopy(data, 
    16, temp, 04);
            itemCount 
    = Util.getInt(temp);
            log.log(Level.INFO, 
    "標簽項目數:" + itemCount);
        }

        
    private void checkFlag() {
            
    byte[] temp = new byte[4];
            System.arraycopy(data, 
    20, temp, 04);
            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, 
    04);
                length 
    = Util.getInt(temp);
                System.arraycopy(data, offset 
    + 4, temp, 04);
                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

    FeedBack:
    # re: YOYOPlayer開發手記(三)APEv2標簽讀寫[未登錄]
    2008-01-10 09:03 | llei
    兄臺做得不錯  回復  更多評論
      
    # re: YOYOPlayer開發手記(三)APEv2標簽讀寫
    2008-02-11 14:28 | 九州
    我喜歡你   回復  更多評論
      
    # re: YOYOPlayer開發手記(三)APEv2標簽讀寫
    2008-03-10 21:35 | jht
    讀了一下,覺得不錯,有機會要用用看!  回復  更多評論
      
    # re: YOYOPlayer開發手記(三)APEv2標簽讀寫
    2008-08-01 11:32 | valimus
    不錯  回復  更多評論
      
    # re: YOYOPlayer開發手記(三)APEv2標簽讀寫[未登錄]
    2012-08-29 10:32 | yuan
    謝謝!  回復  更多評論
      
    主站蜘蛛池模板: 亚洲欧洲日产国码在线观看| 91在线亚洲精品专区| 亚洲乱码中文论理电影| 日韩中文字幕免费视频| 日韩亚洲人成在线综合日本| 中文字幕一区二区免费| 亚洲第一成年男人的天堂| 永久在线观看免费视频| 亚洲av日韩av激情亚洲| 日韩成人免费视频| 亚洲卡一卡2卡三卡4卡无卡三| 久久精品电影免费动漫| 亚洲美女视频免费| 岛国av无码免费无禁网站| 亚洲熟妇自偷自拍另欧美| 国产成人无码免费视频97| 国产亚洲精品成人久久网站| 亚洲伊人久久综合中文成人网| 91av免费在线视频| 亚洲国产成人久久综合一 | 亚洲国产精品精华液| 日韩激情淫片免费看| 特级毛片免费观看视频| 久久影视国产亚洲| 99国产精品视频免费观看| 亚洲一本之道高清乱码| 国产yw855.c免费视频| 精品乱子伦一区二区三区高清免费播放 | www.亚洲精品.com| 久久免费高清视频| 性xxxx黑人与亚洲| 亚洲精品成人网久久久久久| 9久热这里只有精品免费| 亚洲白色白色在线播放| 国产猛烈高潮尖叫视频免费| a级片免费在线观看| 亚洲综合激情五月丁香六月| 亚洲人成电影在线播放| 亚洲一区二区三区免费观看| WWW亚洲色大成网络.COM| 久久精品国产亚洲av成人|