千里冰封
          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 千里冰封 閱讀(5835) 評論(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
          謝謝!  回復  更多評論
            
          主站蜘蛛池模板: 鄂伦春自治旗| 潞西市| 贞丰县| 梨树县| 明星| 辽阳市| 关岭| 无为县| 合肥市| 前郭尔| 陕西省| 普格县| 长岭县| 泊头市| 鹤庆县| 平原县| 江孜县| 鹤壁市| 手游| 池州市| 庆阳市| 抚州市| 甘孜县| 房山区| 福鼎市| 柳州市| 毕节市| 通渭县| 萝北县| 鸡东县| 高邮市| 绍兴县| 阳信县| 诸城市| 全南县| 霍邱县| 平定县| 南木林县| 定襄县| 那曲县| 广饶县|