JAVA写的MP3标签读写器
前几天写了一个NB的音乐插件,自己用了一下,还是挺方便的,后来想想,如果能把歌词也显示出来那就更好了。呵呵,怎么办呢,只有自己写了,在显示歌词之前,必须要知道目前正在播放的MP3是什么内容啊,一点可以从文件名得到一些信息,还有一点就是从MP3文件里面得到这个MP3的信息,我这里实现的ID3V1的格式标签,APEV2也想实现,无奈找不到相关的资料,不知道APEV2的数据结构是怎么样的,所以也无从分析。目前已经写完了ID3V1格式标签的读取和写入。并且NB的音乐插件也实现了本地歌词的搜索,先把ID3V1的文件结构的类文件帖一下,大家一起分享。
MP3的ID3V1的信息结构是很有规律的,它一般是出现在MP3文件的最后128个字节上,并且是以“TAG”开头。我这里把它封装成一个类了。
截图如下:
代码如下:
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.hadeslee.music;
/**
* 一个歌曲信息的类的结构表示
* 这个歌曲是使用ID3V1的信息存储结构的
* @author hadeslee
*/
public class SongInfo {
private final String TAG = " TAG " ; // 文件头1-3
private String songName; // 歌曲名4-33
private String artist; // 歌手名34-63
private String album; // 专辑名61-93
private String year; // 年94-97
private String comment; // 备注98-125
private byte r1, r2, r3; // 三个保留位126,127,128
private boolean valid; // 是否合法
public transient String fileName; // 此歌曲对应的文件名,没有封装
public SongInfo( byte [] data) {
if (data.length != 128 ) {
throw new RuntimeException( " 数据长度不合法: " + data.length);
}
String tag = new String(data, 0 , 3 );
// 只有前三个字节是TAG才处理后面的字节
if (tag.equalsIgnoreCase( " TAG " )) {
valid = true ;
songName = new String(data, 3 , 30 ).trim();
artist = new String(data, 33 , 30 ).trim();
album = new String(data, 63 , 30 ).trim();
year = new String(data, 93 , 4 ).trim();
comment = new String(data, 97 , 28 ).trim();
r1 = data[ 125 ];
r2 = data[ 126 ];
r3 = data[ 127 ];
} else {
valid = false ;
}
}
public SongInfo() {
}
/**
* 返回是否合法
* @return 是否
*/
public boolean isValid() {
return valid;
}
/**
* 得到此对象的128个字节的表示形式
* @return
*/
public byte [] getBytes() {
byte [] data = new byte [ 128 ];
System.arraycopy(TAG.getBytes(), 0 , data, 0 , 3 );
byte [] temp = songName.getBytes();
System.arraycopy(temp, 0 , data, 3 , temp.length > 30 ? 30 : temp.length);
temp = artist.getBytes();
System.arraycopy(temp, 0 , data, 33 , temp.length > 30 ? 30 : temp.length);
temp = album.getBytes();
System.arraycopy(temp, 0 , data, 63 , temp.length > 30 ? 30 : temp.length);
temp = year.getBytes();
System.arraycopy(temp, 0 , data, 93 , temp.length > 4 ? 4 : temp.length);
temp = comment.getBytes();
System.arraycopy(temp, 0 , data, 97 , temp.length > 28 ? 28 : temp.length);
data[ 125 ] = r1;
data[ 126 ] = r2;
data[ 127 ] = r3;
return data;
}
public String getArtist() {
return artist;
}
public void setArtist(String authorName) {
this .artist = authorName;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this .comment = comment;
}
public byte getR1() {
return r1;
}
public void setR1( byte r1) {
this .r1 = r1;
}
public byte getR2() {
return r2;
}
public void setR2( byte r2) {
this .r2 = r2;
}
public byte getR3() {
return r3;
}
public void setR3( byte r3) {
this .r3 = r3;
}
public String getSongName() {
return songName;
}
public void setSongName(String songName) {
if (songName == null ){
throw new NullPointerException( " 歌名不能是null! " );
}
valid = true ;
this .songName = songName;
}
public String getAlbum() {
return album;
}
public void setAlbum(String specialName) {
this .album = specialName;
}
public String getYear() {
return year;
}
public void setYear(String year) {
this .year = year;
}
}
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.hadeslee.music;
/**
* 一个歌曲信息的类的结构表示
* 这个歌曲是使用ID3V1的信息存储结构的
* @author hadeslee
*/
public class SongInfo {
private final String TAG = " TAG " ; // 文件头1-3
private String songName; // 歌曲名4-33
private String artist; // 歌手名34-63
private String album; // 专辑名61-93
private String year; // 年94-97
private String comment; // 备注98-125
private byte r1, r2, r3; // 三个保留位126,127,128
private boolean valid; // 是否合法
public transient String fileName; // 此歌曲对应的文件名,没有封装
public SongInfo( byte [] data) {
if (data.length != 128 ) {
throw new RuntimeException( " 数据长度不合法: " + data.length);
}
String tag = new String(data, 0 , 3 );
// 只有前三个字节是TAG才处理后面的字节
if (tag.equalsIgnoreCase( " TAG " )) {
valid = true ;
songName = new String(data, 3 , 30 ).trim();
artist = new String(data, 33 , 30 ).trim();
album = new String(data, 63 , 30 ).trim();
year = new String(data, 93 , 4 ).trim();
comment = new String(data, 97 , 28 ).trim();
r1 = data[ 125 ];
r2 = data[ 126 ];
r3 = data[ 127 ];
} else {
valid = false ;
}
}
public SongInfo() {
}
/**
* 返回是否合法
* @return 是否
*/
public boolean isValid() {
return valid;
}
/**
* 得到此对象的128个字节的表示形式
* @return
*/
public byte [] getBytes() {
byte [] data = new byte [ 128 ];
System.arraycopy(TAG.getBytes(), 0 , data, 0 , 3 );
byte [] temp = songName.getBytes();
System.arraycopy(temp, 0 , data, 3 , temp.length > 30 ? 30 : temp.length);
temp = artist.getBytes();
System.arraycopy(temp, 0 , data, 33 , temp.length > 30 ? 30 : temp.length);
temp = album.getBytes();
System.arraycopy(temp, 0 , data, 63 , temp.length > 30 ? 30 : temp.length);
temp = year.getBytes();
System.arraycopy(temp, 0 , data, 93 , temp.length > 4 ? 4 : temp.length);
temp = comment.getBytes();
System.arraycopy(temp, 0 , data, 97 , temp.length > 28 ? 28 : temp.length);
data[ 125 ] = r1;
data[ 126 ] = r2;
data[ 127 ] = r3;
return data;
}
public String getArtist() {
return artist;
}
public void setArtist(String authorName) {
this .artist = authorName;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this .comment = comment;
}
public byte getR1() {
return r1;
}
public void setR1( byte r1) {
this .r1 = r1;
}
public byte getR2() {
return r2;
}
public void setR2( byte r2) {
this .r2 = r2;
}
public byte getR3() {
return r3;
}
public void setR3( byte r3) {
this .r3 = r3;
}
public String getSongName() {
return songName;
}
public void setSongName(String songName) {
if (songName == null ){
throw new NullPointerException( " 歌名不能是null! " );
}
valid = true ;
this .songName = songName;
}
public String getAlbum() {
return album;
}
public void setAlbum(String specialName) {
this .album = specialName;
}
public String getYear() {
return year;
}
public void setYear(String year) {
this .year = year;
}
}
编辑对话框的代码:
/*
* SongInfoDialog.java
*
* Created on 2007年11月26日, 下午4:12
*/
package com.hadeslee.music;
import java.awt.Dialog;
import java.awt.Frame;
import java.io.File;
import java.io.RandomAccessFile;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author hadeslee
*/
public class SongInfoDialog extends javax.swing.JDialog {
private SongInfo info; // 歌曲信息对象
private File file; // 文件对象
private boolean valid; // 表示原来这个文件是不是合法的,如果不是,就要重新写入128个字节
/** Creates new form SongInfoDialog */
public SongInfoDialog(java.awt.Frame parent, boolean modal) {
super (parent, modal);
initComponents();
}
public SongInfoDialog(Dialog parent, boolean modal) {
super (parent, modal);
initComponents();
}
public SongInfoDialog(Frame parent, boolean modal, File file) {
this (parent, modal);
this .file = file;
init();
}
public SongInfoDialog(Dialog parent, boolean modal, File file) {
this (parent, modal);
this .file = file;
init();
}
/**
* 初始化
*/
private void init() {
try {
fileName.setText(file.toString());
RandomAccessFile ra = new RandomAccessFile(file, " r " );
byte [] buffer = new byte [ 128 ];
ra.seek(ra.length() - 128 );
ra.read(buffer);
info = new SongInfo(buffer);
valid = info.isValid();
title.setText(info.getSongName());
artist.setText(info.getArtist());
album.setText(info.getAlbum());
year.setText(info.getYear());
comment.setText(info.getComment());
r2.setText( "" + info.getR2());
r3.setText( "" + info.getR3());
ra.close();
} catch (Exception ex) {
Logger.getLogger(SongInfoDialog. class .getName()).log(Level.SEVERE, null , ex);
}
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {
jLabel5 = new javax.swing.JLabel();
fileName = new javax.swing.JTextField();
jPanel1 = new javax.swing.JPanel();
jLabel1 = new javax.swing.JLabel();
title = new javax.swing.JTextField();
jLabel2 = new javax.swing.JLabel();
artist = new javax.swing.JTextField();
jLabel3 = new javax.swing.JLabel();
jLabel4 = new javax.swing.JLabel();
album = new javax.swing.JTextField();
jLabel6 = new javax.swing.JLabel();
r2 = new javax.swing.JTextField();
year = new javax.swing.JTextField();
jLabel7 = new javax.swing.JLabel();
r3 = new javax.swing.JTextField();
jLabel8 = new javax.swing.JLabel();
jScrollPane1 = new javax.swing.JScrollPane();
comment = new javax.swing.JTextArea();
jButton1 = new javax.swing.JButton();
jButton2 = new javax.swing.JButton();
jButton3 = new javax.swing.JButton();
jLabel9 = new javax.swing.JLabel();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
jLabel5.setText( " 文件名: " );
fileName.setEditable( false );
jPanel1.setBorder(javax.swing.BorderFactory.createTitledBorder( " 标签 " ));
jLabel1.setText( " 标 题: " );
jLabel2.setText( " 艺术家: " );
jLabel3.setText( " 专 辑: " );
jLabel4.setText( " 年份: " );
jLabel6.setText( " 音轨: " );
r2.setEditable( false );
jLabel7.setText( " 流 派: " );
r3.setEditable( false );
jLabel8.setText( " 备 注: " );
comment.setColumns( 20 );
comment.setRows( 5 );
jScrollPane1.setViewportView(comment);
javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
jPanel1.setLayout(jPanel1Layout);
jPanel1Layout.setHorizontalGroup(
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addContainerGap()
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addComponent(jLabel1)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(title, javax.swing.GroupLayout.PREFERRED_SIZE, 183 , javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(jPanel1Layout.createSequentialGroup()
.addComponent(jLabel2)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(artist, javax.swing.GroupLayout.DEFAULT_SIZE, 185 , Short.MAX_VALUE)
.addContainerGap())
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup()
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addComponent(jLabel3)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(album, javax.swing.GroupLayout.DEFAULT_SIZE, 110 , Short.MAX_VALUE))
.addGroup(jPanel1Layout.createSequentialGroup()
.addComponent(jLabel7)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(r3, javax.swing.GroupLayout.DEFAULT_SIZE, 110 , Short.MAX_VALUE)))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addComponent(jLabel4)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(year, javax.swing.GroupLayout.PREFERRED_SIZE, 31 , javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(jPanel1Layout.createSequentialGroup()
.addComponent(jLabel6)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(r2, javax.swing.GroupLayout.PREFERRED_SIZE, 30 , javax.swing.GroupLayout.PREFERRED_SIZE)))
.addContainerGap())
.addGroup(jPanel1Layout.createSequentialGroup()
.addComponent(jLabel8)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 185 , Short.MAX_VALUE)
.addContainerGap())))
);
jPanel1Layout.setVerticalGroup(
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel1)
.addComponent(title, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel2)
.addComponent(artist, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel3)
.addComponent(r2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(jLabel6)
.addComponent(album, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel4)
.addComponent(year, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(jLabel7)
.addComponent(r3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addComponent(jLabel8)
.addContainerGap( 42 , Short.MAX_VALUE))
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 57 , Short.MAX_VALUE)))
);
jButton1.setText( " 重新读取文件 " );
jButton1.addActionListener( new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton1ActionPerformed(evt);
}
});
jButton2.setText( " 保存到文件 " );
jButton2.addActionListener( new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton2ActionPerformed(evt);
}
});
jButton3.setText( " 取消 " );
jButton3.addActionListener( new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton3ActionPerformed(evt);
}
});
jLabel9.setForeground( new java.awt.Color( 255 , 0 , 102 ));
jLabel9.setText( " 注:目前只支持ID3v1格式标签的存取 " );
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(jLabel5)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(fileName, javax.swing.GroupLayout.DEFAULT_SIZE, 221 , Short.MAX_VALUE))
.addGroup(layout.createSequentialGroup()
.addComponent(jButton1)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jButton2)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jButton3))
.addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(jLabel9))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel5)
.addComponent(fileName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jButton1)
.addComponent(jButton2)
.addComponent(jButton3))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(jLabel9)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
pack();
} // </editor-fold>
private void jButton3ActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
this .dispose();
}
private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
doSave();
}
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
init();
}
private void doSave() {
try {
info.setAlbum(album.getText());
info.setArtist(artist.getText());
info.setComment(comment.getText());
info.setR1(( byte ) 0 );
try {
info.setR2(Byte.parseByte(r2.getText()));
info.setR3(Byte.parseByte(r3.getText()));
} catch (Exception exe) {
exe.printStackTrace();
}
info.setYear(year.getText());
info.setSongName(title.getText());
RandomAccessFile raf = new RandomAccessFile(file, " rw " );
// 如果这个文件原来就是合法的,那么就不用新起128个字节了
if (valid) {
raf.seek(raf.length() - 128 );
} else { // 否则就在末层加上128个字节
raf.seek(raf.length());
}
raf.write(info.getBytes());
raf.close();
this .dispose();
} catch (Exception ex) {
Logger.getLogger(SongInfoDialog. class .getName()).log(Level.SEVERE, null , ex);
}
}
/**
* @param args the command line arguments
*/
public static void main(String args[]) {
java.awt.EventQueue.invokeLater( new Runnable() {
public void run() {
SongInfoDialog dialog = new SongInfoDialog( new javax.swing.JFrame(), true );
dialog.addWindowListener( new java.awt.event.WindowAdapter() {
public void windowClosing(java.awt.event.WindowEvent e) {
System.exit( 0 );
}
});
dialog.setVisible( true );
}
});
}
// Variables declaration - do not modify
private javax.swing.JTextField album;
private javax.swing.JTextField artist;
private javax.swing.JTextArea comment;
private javax.swing.JTextField fileName;
private javax.swing.JButton jButton1;
private javax.swing.JButton jButton2;
private javax.swing.JButton jButton3;
private javax.swing.JLabel jLabel1;
private javax.swing.JLabel jLabel2;
private javax.swing.JLabel jLabel3;
private javax.swing.JLabel jLabel4;
private javax.swing.JLabel jLabel5;
private javax.swing.JLabel jLabel6;
private javax.swing.JLabel jLabel7;
private javax.swing.JLabel jLabel8;
private javax.swing.JLabel jLabel9;
private javax.swing.JPanel jPanel1;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JTextField r2;
private javax.swing.JTextField r3;
private javax.swing.JTextField title;
private javax.swing.JTextField year;
// End of variables declaration
}
* SongInfoDialog.java
*
* Created on 2007年11月26日, 下午4:12
*/
package com.hadeslee.music;
import java.awt.Dialog;
import java.awt.Frame;
import java.io.File;
import java.io.RandomAccessFile;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author hadeslee
*/
public class SongInfoDialog extends javax.swing.JDialog {
private SongInfo info; // 歌曲信息对象
private File file; // 文件对象
private boolean valid; // 表示原来这个文件是不是合法的,如果不是,就要重新写入128个字节
/** Creates new form SongInfoDialog */
public SongInfoDialog(java.awt.Frame parent, boolean modal) {
super (parent, modal);
initComponents();
}
public SongInfoDialog(Dialog parent, boolean modal) {
super (parent, modal);
initComponents();
}
public SongInfoDialog(Frame parent, boolean modal, File file) {
this (parent, modal);
this .file = file;
init();
}
public SongInfoDialog(Dialog parent, boolean modal, File file) {
this (parent, modal);
this .file = file;
init();
}
/**
* 初始化
*/
private void init() {
try {
fileName.setText(file.toString());
RandomAccessFile ra = new RandomAccessFile(file, " r " );
byte [] buffer = new byte [ 128 ];
ra.seek(ra.length() - 128 );
ra.read(buffer);
info = new SongInfo(buffer);
valid = info.isValid();
title.setText(info.getSongName());
artist.setText(info.getArtist());
album.setText(info.getAlbum());
year.setText(info.getYear());
comment.setText(info.getComment());
r2.setText( "" + info.getR2());
r3.setText( "" + info.getR3());
ra.close();
} catch (Exception ex) {
Logger.getLogger(SongInfoDialog. class .getName()).log(Level.SEVERE, null , ex);
}
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {
jLabel5 = new javax.swing.JLabel();
fileName = new javax.swing.JTextField();
jPanel1 = new javax.swing.JPanel();
jLabel1 = new javax.swing.JLabel();
title = new javax.swing.JTextField();
jLabel2 = new javax.swing.JLabel();
artist = new javax.swing.JTextField();
jLabel3 = new javax.swing.JLabel();
jLabel4 = new javax.swing.JLabel();
album = new javax.swing.JTextField();
jLabel6 = new javax.swing.JLabel();
r2 = new javax.swing.JTextField();
year = new javax.swing.JTextField();
jLabel7 = new javax.swing.JLabel();
r3 = new javax.swing.JTextField();
jLabel8 = new javax.swing.JLabel();
jScrollPane1 = new javax.swing.JScrollPane();
comment = new javax.swing.JTextArea();
jButton1 = new javax.swing.JButton();
jButton2 = new javax.swing.JButton();
jButton3 = new javax.swing.JButton();
jLabel9 = new javax.swing.JLabel();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
jLabel5.setText( " 文件名: " );
fileName.setEditable( false );
jPanel1.setBorder(javax.swing.BorderFactory.createTitledBorder( " 标签 " ));
jLabel1.setText( " 标 题: " );
jLabel2.setText( " 艺术家: " );
jLabel3.setText( " 专 辑: " );
jLabel4.setText( " 年份: " );
jLabel6.setText( " 音轨: " );
r2.setEditable( false );
jLabel7.setText( " 流 派: " );
r3.setEditable( false );
jLabel8.setText( " 备 注: " );
comment.setColumns( 20 );
comment.setRows( 5 );
jScrollPane1.setViewportView(comment);
javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
jPanel1.setLayout(jPanel1Layout);
jPanel1Layout.setHorizontalGroup(
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addContainerGap()
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addComponent(jLabel1)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(title, javax.swing.GroupLayout.PREFERRED_SIZE, 183 , javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(jPanel1Layout.createSequentialGroup()
.addComponent(jLabel2)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(artist, javax.swing.GroupLayout.DEFAULT_SIZE, 185 , Short.MAX_VALUE)
.addContainerGap())
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup()
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addComponent(jLabel3)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(album, javax.swing.GroupLayout.DEFAULT_SIZE, 110 , Short.MAX_VALUE))
.addGroup(jPanel1Layout.createSequentialGroup()
.addComponent(jLabel7)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(r3, javax.swing.GroupLayout.DEFAULT_SIZE, 110 , Short.MAX_VALUE)))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addComponent(jLabel4)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(year, javax.swing.GroupLayout.PREFERRED_SIZE, 31 , javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(jPanel1Layout.createSequentialGroup()
.addComponent(jLabel6)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(r2, javax.swing.GroupLayout.PREFERRED_SIZE, 30 , javax.swing.GroupLayout.PREFERRED_SIZE)))
.addContainerGap())
.addGroup(jPanel1Layout.createSequentialGroup()
.addComponent(jLabel8)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 185 , Short.MAX_VALUE)
.addContainerGap())))
);
jPanel1Layout.setVerticalGroup(
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel1)
.addComponent(title, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel2)
.addComponent(artist, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel3)
.addComponent(r2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(jLabel6)
.addComponent(album, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel4)
.addComponent(year, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(jLabel7)
.addComponent(r3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addComponent(jLabel8)
.addContainerGap( 42 , Short.MAX_VALUE))
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 57 , Short.MAX_VALUE)))
);
jButton1.setText( " 重新读取文件 " );
jButton1.addActionListener( new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton1ActionPerformed(evt);
}
});
jButton2.setText( " 保存到文件 " );
jButton2.addActionListener( new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton2ActionPerformed(evt);
}
});
jButton3.setText( " 取消 " );
jButton3.addActionListener( new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton3ActionPerformed(evt);
}
});
jLabel9.setForeground( new java.awt.Color( 255 , 0 , 102 ));
jLabel9.setText( " 注:目前只支持ID3v1格式标签的存取 " );
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(jLabel5)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(fileName, javax.swing.GroupLayout.DEFAULT_SIZE, 221 , Short.MAX_VALUE))
.addGroup(layout.createSequentialGroup()
.addComponent(jButton1)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jButton2)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jButton3))
.addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(jLabel9))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel5)
.addComponent(fileName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jButton1)
.addComponent(jButton2)
.addComponent(jButton3))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(jLabel9)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
pack();
} // </editor-fold>
private void jButton3ActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
this .dispose();
}
private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
doSave();
}
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
init();
}
private void doSave() {
try {
info.setAlbum(album.getText());
info.setArtist(artist.getText());
info.setComment(comment.getText());
info.setR1(( byte ) 0 );
try {
info.setR2(Byte.parseByte(r2.getText()));
info.setR3(Byte.parseByte(r3.getText()));
} catch (Exception exe) {
exe.printStackTrace();
}
info.setYear(year.getText());
info.setSongName(title.getText());
RandomAccessFile raf = new RandomAccessFile(file, " rw " );
// 如果这个文件原来就是合法的,那么就不用新起128个字节了
if (valid) {
raf.seek(raf.length() - 128 );
} else { // 否则就在末层加上128个字节
raf.seek(raf.length());
}
raf.write(info.getBytes());
raf.close();
this .dispose();
} catch (Exception ex) {
Logger.getLogger(SongInfoDialog. class .getName()).log(Level.SEVERE, null , ex);
}
}
/**
* @param args the command line arguments
*/
public static void main(String args[]) {
java.awt.EventQueue.invokeLater( new Runnable() {
public void run() {
SongInfoDialog dialog = new SongInfoDialog( new javax.swing.JFrame(), true );
dialog.addWindowListener( new java.awt.event.WindowAdapter() {
public void windowClosing(java.awt.event.WindowEvent e) {
System.exit( 0 );
}
});
dialog.setVisible( true );
}
});
}
// Variables declaration - do not modify
private javax.swing.JTextField album;
private javax.swing.JTextField artist;
private javax.swing.JTextArea comment;
private javax.swing.JTextField fileName;
private javax.swing.JButton jButton1;
private javax.swing.JButton jButton2;
private javax.swing.JButton jButton3;
private javax.swing.JLabel jLabel1;
private javax.swing.JLabel jLabel2;
private javax.swing.JLabel jLabel3;
private javax.swing.JLabel jLabel4;
private javax.swing.JLabel jLabel5;
private javax.swing.JLabel jLabel6;
private javax.swing.JLabel jLabel7;
private javax.swing.JLabel jLabel8;
private javax.swing.JLabel jLabel9;
private javax.swing.JPanel jPanel1;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JTextField r2;
private javax.swing.JTextField r3;
private javax.swing.JTextField title;
private javax.swing.JTextField year;
// End of variables declaration
}
有歌词的插件,由于还不是很完善,所以过一两天再发布,呵呵。:)
尽管千里冰封
依然拥有晴空
你我共同品味JAVA的浓香.