// ID3.j?ava
//
// $Id: ID3.java,v 1.4 2008/01/03 04:35:51 dmitriy Exp $
//
// de.vdheide.mp3: Access MP3 properties, ID3 and ID3v2 tags
// Copyright (C) 1999 Jens Vonderheide <jens@vdheide.de>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the
// Free Software Foundation, Inc., 59 Temple Place - Suite 330,
// Boston, MA 02111-1307, USA.
/**
* Class to read and modify ID3 tags on MP3 files.
*
* ID3 information are loaded
* - the first time any of these is requested
* - after doing a readTag()
* - after changing any of these (no real reload, it is just changed)
* ID3 information are written
* - after doing a writeTag()
*
*
* If a file does not contain an ID3 tag, each read access will throw
* a NoID3TagException. A write access will create an ID3 tag if none
* is present.
*/
package de.vdheide.mp3;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.Serializable;
public class ID3 implements Serializable
{
public static final String ISO_8859_1 = "iso-8859-1";
// encoding to use when converting from Unicode (String) to bytes
protected String encoding = ISO_8859_1;
/**
* Create a new ID3 tag which is based on mp3_file
*
* @param mp3_file MP3 file to read ID3 tag to / write ID3 tag to
*/
public ID3(File mp3_file)
{
this(mp3_file, null);
}
public ID3(File mp3_file, String encoding)
{
this.mp3_file=mp3_file;
if (encoding != null)
this.encoding = encoding;
}
public void setEncoding(String encoding)
{
this.encoding = encoding;
}
public String getEncoding()
{
return encoding;
}
/**
* Read title from ID3 tag
*
* @returns Title
* @exception NoID3TagException If file does not contain an ID3 tag
*/
public String getTitle() throws NoID3TagException
{
try {
checkIfRead(title);
} catch (IOException e) {
throw new NoID3TagException();
}
return title;
}
/**
* Read artist from ID3 tag
*
* @returns Artist
* @exception NoID3TagException If file does not contain an ID3 tag
*/
public String getArtist() throws NoID3TagException
{
try {
checkIfRead(artist);
} catch (IOException e) {
throw new NoID3TagException();
}
return artist;
}
/**
* Read album from ID3 tag
*
* @returns album
* @exception NoID3TagException If file does not contain an ID3 tag
*/
public String getAlbum() throws NoID3TagException
{
try {
checkIfRead(album);
} catch (IOException e) {
throw new NoID3TagException();
}
return album;
}
/**
* Read year from ID3 tag
*
* @returns Year
* @exception NoID3TagException If file does not contain an ID3 tag
*/
public String getYear() throws NoID3TagException
{
try {
checkIfRead(year);
} catch (IOException e) {
throw new NoID3TagException();
}
return year;
}
/**
* Read genre from ID3 tag
*
* @returns Genre
* @exception NoID3TagException If file does not contain an ID3 tag
*/
public int getGenre() throws NoID3TagException
{
if (genre==null) {
// ID3 tag not already read
// read tag
try {
readTag();
} catch (IOException e) {
throw new NoID3TagException();
}
}
return genre.byteValue();
}
/**
* Read comment from ID3 tag
*
* @returns comment
* @exception NoID3TagException If file does not contain an ID3 tag
*/
public String getComment() throws NoID3TagException
{
try {
checkIfRead(comment);
return comment;
} catch (IOException e) {
throw new NoID3TagException();
}
}
/**
* Read track number from ID3 tag
*
* @returns Track number
* @exception NoID3TagException If file does not contain an ID3 tag
*/
public int getTrack() throws NoID3TagException
{
if (track == null) {
try {
readTag();
} catch (IOException e) {
throw new NoID3TagException();
}
}
return track.byteValue();
}
/**
* Read ID3 tag and prepare for retrieval with getXXX
* Use this method to reread tag if changed externally
*
* @exception NoID3TagException If file does not contain an ID3 tag
* @exception IOException If I/O error occurs
*/
public void readTag() throws NoID3TagException, IOException
{
// TODO multiple ID3 tags can be attached to file including unicoded starting from FE FF
//
// get access to file
RandomAccessFile in = new RandomAccessFile(mp3_file, "r");
// file is now prepared
// check for ID3 tag
try {
if (checkForTag()==false) {
// No ID3 tag found
throw new NoID3TagException();
} else {
// ID3 tag found, read it
in.seek(in.length()-125);
byte []buffer = new byte[125];
if (in.read(buffer,0, 125)!=125) {
// tag too short
// this cannot happen cause we found "TAG" at correct position
}
// cut tag;
title = new String(buffer, 0, 30, encoding).trim();
artist = new String(buffer, 30, 30, encoding).trim();
album = new String(buffer, 60, 30, encoding).trim();
year = new String(buffer, 90, 4, encoding).trim();
comment = new String(buffer, 94, 29, encoding).trim();
track = new Byte(buffer[123]);
genre = new Byte(buffer[124]);
}
}
finally
{
in.close();
}
}
/**
* Set title
*
* @param title Title
*/
public void setTitle(String title)
{
this.title=title;
}
/**
* Set artist
*
* @param artist Artist
*/
public void setArtist(String artist)
{
this.artist=artist;
}
/**
* Set album
*
* @param album Album
*/
public void setAlbum(String album)
{
this.album=album;
}
/**
* Set year
*
* @param year Year
*/
public void setYear(String year)
{
this.year=year;
}
/**
* Set comment
*
* @param comment Comment
*/
public void setComment(String comment)
{
this.comment=comment;
}
/**
* Set track number
*
* @param track Track number
* @exception ID3IllegalFormatException if track is negative or
* larger than 255
*/
public void setTrack(int track) throws ID3IllegalFormatException
{
if (track<0 || track>255) {
throw new ID3IllegalFormatException();
} else {
this.track = new Byte((byte)track);
}
}
/**
* Set genre
*
* @param genre Genre
* @exception ID3IllegalFormatException if genre is negative or
* larger than 255
*/
public void setGenre(int genre) throws ID3IllegalFormatException
{
this.genre=new Byte((byte)genre);
}
/**
* Write information provided with setXXX to ID3 tag
*/
public void writeTag() throws IOException
{
// get access to file
RandomAccessFile in = new RandomAccessFile(mp3_file, "rw");
// file is now prepared
// check for ID3 tag
if (checkForTag()==false) {
// No ID3 tag found, create new
// seek to end of file
in.seek(in.length());
} else {
// jump to "TAG"
in.seek(in.length()-128);
}
// write new tag
in.write(new String("TAG").getBytes(encoding));
in.write(fillWithNills(title, 30).getBytes(encoding));
in.write(fillWithNills(artist, 30).getBytes(encoding));
in.write(fillWithNills(album, 30).getBytes(encoding));
in.write(fillWithNills(year, 4).getBytes(encoding));
in.write(fillWithNills(comment, 29).getBytes(encoding));
if (track == null) {
in.writeByte(0);
} else {
in.writeByte(track.byteValue());
}
if (genre == null) {
in.writeByte(0);
} else {
in.writeByte(genre.byteValue());
}
in.close();
}
private File mp3_file = null; // file to access
private String title=null; // id3 title
private String artist=null; // id3 artist
private String album=null; // id3 album
private String year=null; // id3 year
private Byte genre = null; // id3 genre, -1==not set
private String comment=null; // id3 comment
private Byte track = null; // id3 track number
/**
* Check if reading of ID3 tag if necessary. If so, reads tag.
*
* @param what Which information is requested?
* @exception NoID3TagException If file does not contain an ID3 tag
* @exception IOException If an I/O errors occurs
*/
private void checkIfRead(String what) throws NoID3TagException, IOException
{
if (what==null) {
readTag();
}
}
/**
* Check if ID3 tag is present
*
* @returns true if tag present
*/
public boolean checkForTag() throws IOException
{
// Create random access file
RandomAccessFile raf = new RandomAccessFile(mp3_file, "r");
try {
if (raf.length()<129) {
// file to short for an ID3 tag
return false;
} else {
// go to position where "TAG" must be
long seekPos = raf.length()-128;
raf.seek(seekPos);
byte buffer[] = new byte[3];
if (raf.read(buffer,0,3)!=3) {
// something terrible happened
throw new IOException("Read beyond end of file");
}
String testTag=new String(buffer,0,3,encoding);
if (!testTag.equals("TAG")) {
return false;
} else {
return true;
}
}
} finally {
raf.close();
}
}
/**
* Fill <tt>str</tt> with \0 until <tt>str</tt> has length <tt>len</tt>
*
* @param str String to work with
* @param len Length of <tt>str</tt> after filling
* @returns Filled string
*/
private String fillWithNills(String str, int len)
{
if (str==null) {
// tag info not set!
str=new String("");
}
StringBuffer tmp = new StringBuffer(str);
for (int i=str.length()+1; i<=len; i++) {
tmp.append('\0');
}
return tmp.toString();
}
}