uk.org.retep.util.io.MP3 Maven / Gradle / Ivy
/*
* Copyright (c) 1998-2007, Peter T Mount
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of the retep.org.uk nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
*
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package uk.org.retep.util.io;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* This class reads in the TAG's contained within an MP3 audio files header.
* This could then be used to populate a database with your MP3 collection.
*
* @author Peter T Mount
* @version 1.0
*/
public class MP3
implements Serializable
{
static final long serialVersionUID = 3046921477852383265L;
private Map props =
new HashMap( );
/**
* Tag name containing the year the track was produced
*/
public static final String YEAR = "TYER";
/**
* Tag name containing the album name
*/
public static final String ALBUM = "TALB";
/**
* Tag name containing the source media
*/
public static final String MED = "TMED";
/**
* The track number (ie 01-99 for tracks on a cd)
*/
public static final String TRACK = "TRCK";
/**
* The track length in bytes
*/
public static final String LENGTH = "TLEN";
/**
* The URL for the artist
*/
public static final String URL_ARTIST = "WOAR";
/**
* The URL to buy the cd
*/
public static final String URL_BUYCD = "WCOM";
/**
* The URL for audio?
*/
public static final String URL_AUDIO = "WOAF";
/**
* The title of this track
*/
public static final String TITLE = "TIT2";
/**
* The Lyrics for this track
*/
public static final String LYRICS = "USLT";
/**
* The Genre
*/
public static final String GENRE = "TCON";
/**
* This is unique to MusicMatch. The Situation (ie Dance, Romantic etc)
*/
public static final String SITUATION = "MusicMatch_Situation";
/**
* This is unique to MusicMatch. The Tempo
*/
public static final String TEMPO = "MusicMatch_Tempo";
/**
* This is unique to MusicMatch? Notes
*/
public static final String NOTES = "notes";
/**
* This is unique to MusicMatch. The Mood.
*/
public static final String MOOD = "MusicMatch_Mood";
/**
* This is unique to MusicMatch. The Biography
*/
public static final String BIO = "MusicMatch_Bio";
/**
* Empty Constructor. Does nothing inparticular.
*/
public MP3( )
{
}
/**
* Construct and read data in from a file
* @param aFile File pointing to an MP3 file
* @exception IOException
*/
public MP3( File aFile )
throws IOException
{
read( aFile );
}
/**
* Construct and read data in from a file
* @param raf RandomAccessFile pointing to an MP3 file
* @exception IOException
*/
public MP3( RandomAccessFile raf )
throws IOException
{
read( raf );
}
/**
* @param tag A tag name from within the MP3 file
* @return the value of that tag, null if not present.
*/
public String get( String tag )
{
return props.get( tag );
}
/**
* Obtains an Iterator of tag names in the MP3 file.
* @return Iterator
*/
public Iterator getTags( )
{
return props.keySet( ).iterator( );
}
/**
* Read data in from a file
* @param aFile File pointing to an MP3 file
* @exception IOException
*/
public void read( File aFile )
throws IOException
{
RandomAccessFile raf = new RandomAccessFile( aFile, "r" );
read( raf );
raf.close( );
}
/**
* Read data in from a file
* @param raf RandomAccessFile pointing to an MP3 file
* @exception IOException
*/
public void read( RandomAccessFile raf )
throws IOException
{
props.clear( );
byte[] b = new byte[2048];
// File header - this bit is dubious
// Skip the first part of the header
raf.seek( 0 );
raf.read( b, 0, 3 );
raf.read( b, 0, 7 );
// Read in the tags into props
while( ripTag( raf, b ) )
{
;
}
}
/**
* Helper method for read. Reads in a tag, and returns true if there was a tag
* and false if that was the last one.
*/
private boolean ripTag( RandomAccessFile raf, byte[] b )
throws IOException
{
// The first 4 bytes form the tag name
if( raf.read( b, 0, 4 ) < 4 )
{
return false;
}
if( b[0] == 0 && b[1] == 0 && b[2] == 0 && b[3] == 0 )
{
return false;
}
String key = new String( b, 0, 4 );
// The next 4 are the length.
int l = raf.readInt( );
// Now there are 2 more scrap bytes here. I don't know what they are for
raf.read( b, 0, 2 );
// Now the data
byte[] r = new byte[l];
raf.read( r );
// Now The tag COMM is a comment, but MusicMatch adds its own tags using COMM
if( key.equals( "COMM" ) )
{
if( r[0] == 0 && r[1] == 0 && r[2] == 0 && r[3] == 0 )
{
// Find the next 0
int p = 4;
while( p < r.length && r[p] != 0 )
{
p++;
}
int q = p;
while( p < r.length && r[p] == 0 )
{
p++;
}
// Now form a new key value. This enables MusicMatch's own tags to be
// read.
if( p > 3 && p < r.length )
{
key = new String( r, 4, q - 4 );
byte[] s = new byte[l - p];
System.arraycopy( r, p, s, 0, s.length );
r = s;
}
}
}
// Store the result
int p = 0;
while( p < r.length && r[p] == 0 )
{
p++;
}
props.put( key, new String( r, p, r.length - p ) );
return true;
}
/**
* A simple example application. This reads all mp3 files given on the command
* line, and writes their TAG's to System.out.
* @param args
* @throws java.lang.Exception
*/
public static void main( String[] args )
throws Exception
{
for( int i = 0; i < args.length; i++ )
{
MP3 mp3 = new MP3( new File( args[i] ) );
System.out.println( "Details about file: " + args[i] + "\n===================" );
Iterator it = mp3.getTags( );
while( it.hasNext( ) )
{
String tag = (String) it.next();
System.out.println( tag + "\t\"" + mp3.get( tag ) + "\"" );
}
System.out.println( );
}
}
}