Parsing MP3 Basic Info manually
Let’s continue from my previous article on MP3 file structure.
We’re going into practice to parse MP3 basic info: Title, Artist, Year, Album. These are the most basic ones and we can achieve by reading the very last 128 bytes at the end of file.
First you need to put the file into Emulator, in my case, I put into “/sdcard/music.mp3“. After parsing just display it to screen:
Think it simple, just Java, no Android thought here!
This is how I did it 🙂
package pete.android.study; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.util.Calendar; import java.util.HashMap; public class MP3HeaderInfo { // --- { constants } --- >>> // ID3 tag value public static final String ID3_TAG = "TAG"; public static final int BYTE_128 = 128; public static final int[] OFFSET_TAG = new int[] { 0, 3 }; public static final int[] OFFSET_TITLE = new int[] { 3, 33 }; public static final int[] OFFSET_ARTIST = new int[] { 33, 63 }; public static final int[] OFFSET_YEAR = new int[] { 93, 97 }; public static final int[] OFFSET_ALBUM = new int[] { 63, 93 }; // indexer public static final int FROM = 0; public static final int TO = 1; // key attribute public static final String TITLE = "Title"; public static final String ARTIST = "Artist"; public static final String YEAR = "Year"; public static final String ALBUM = "Album"; // default values for each key value public static final String DEFAULT_TITLE = "Unknown Title"; public static final String DEFAULT_ARTIST = "Unknown Artist"; public static final String DEFAULT_YEAR = String.valueOf(Calendar.getInstance().get(Calendar.YEAR)); public static final String DEFAULT_ALBUM = "Unknown Album"; // --- { constants } --- <<< // --- { fields } --- >>> private String mFile; private String mTitle = DEFAULT_TITLE; private String mArtist = DEFAULT_ARTIST; private String mYear = DEFAULT_YEAR; private String mAlbum = DEFAULT_ALBUM; private String mErrorLog; // for error information // --- { fields } --- <<< // --- { constructor } --- >>> public MP3HeaderInfo(String file) { mFile = file; // auto parse file, only once parseAudio(); } // --- { constructor } --- <<< // --- { properties } --- >>> public String getTitle() { return !mTitle.equals("") ? mTitle.trim() : DEFAULT_TITLE; } public String getArtist() { return !mArtist.equals("") ? mArtist.trim() : DEFAULT_ARTIST; } public String getYear() { return !mYear.equals("") ? mYear.trim() : DEFAULT_YEAR; } public String getAlbum() { return !mAlbum.equals("") ? mAlbum.trim() : DEFAULT_ALBUM; } public String getErrorLog() { return mErrorLog; } // --- { properties } --- <<< // --- { private methods } --- >>> private boolean parseAudio() { // create MP3 File object File mp3 = new File(mFile); FileInputStream fis; try { // create new file stream for parsing file in binary fis = new FileInputStream(mp3); // get file size int size = (int) mp3.length(); // offset to the first byte of the last 128 bytes fis.skip(size - BYTE_128); // read chunk of 128 bytes byte[] chunk = new byte[BYTE_128]; fis.read(chunk); // convert chunk to string String id3 = new String(chunk); // get first 3 byte String tag = id3.substring(OFFSET_TAG[FROM], OFFSET_TAG[TO]); // if equals to "TAG" meaning a valid readable one if(tag.equals(ID3_TAG)) { mTitle = id3.substring(OFFSET_TITLE[FROM], OFFSET_TITLE[TO]); mArtist = id3.substring(OFFSET_ARTIST[FROM], OFFSET_ARTIST[TO]); mYear = id3.substring(OFFSET_YEAR[FROM], OFFSET_YEAR[TO]); mAlbum = id3.substring(OFFSET_ALBUM[FROM], OFFSET_ALBUM[TO]); } // close stream after done fis.close(); // return complete return true; } catch (FileNotFoundException e) { // log error mErrorLog = e.getMessage(); return false; } catch (IOException e) { // log error mErrorLog = e.getMessage(); return false; } } // --- { private methods } --- <<< // --- { public methods } --- >>> public static HashMap<String, String> getInfo(String file) { HashMap<String, String> result = new HashMap<String, String>(); MP3HeaderInfo info = new MP3HeaderInfo(file); result.put(ARTIST, info.getArtist()); result.put(TITLE, info.getTitle()); result.put(YEAR, info.getYear()); result.put(ALBUM, info.getAlbum()); return result; } // --- { public methods } --- <<< }
For MainActivity, just input file name to parse and display:
package pete.android.study; import java.util.HashMap; import android.app.Activity; import android.os.Bundle; import android.widget.TextView; public class MainActivity extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); TextView tvMain = (TextView)findViewById(R.id.tvMain); HashMap<String, String> info = MP3HeaderInfo.getInfo("/sdcard/music.mp3"); String out = ""; out += info.get("Title") + " - " + info.get("Year") + "\n"; out += info.get("Album") + "\n" + info.get("Artist") + "\n"; tvMain.setText(out); } }
Very very basic of Java file handling, isn’t it?
Talking about MP3 Song Duration or Bit-rate, well, it’s quite complicated … I’m still not good enough at parsing them at the moment, it would take some more time to study about.
However, I guess those above info is enough to display to users. Or you can even achieve reading more metadata on MP3 file by using third-party libraries for your applications.
Have fun!
Cheers,
Pete Houston
A Quick Study on MP3 file structure
Just had a little time playing around with MP3 file and found it quite interesting.
Do you like “New Divide” by Linkin Park, which is the Original Sound Track on Transformer 2: The Revenge of the Fallen? I personally like it very much. Ah right, Transformer 3 – Dark of the Moon is coming tomorrow, certainly I’ll watch it :D.
If you have the MP3 file of the song, on Windows Explorer right click on it and select Properties, open tab Details:
You can see its information like: Title, Artists, Album, Year, Genre, Length ….
Ok, try to open it using Mp3tag (v2.49):
The tag version of MP3 file is: ID3v2.3, which is basically derived from v1 and v2.3. Let’s have a look at the internal structure of ID3v1:
At the description at original page about ID3v1,
Song Title | 30 characters |
Artist | 30 characters |
Album | 30 characters |
Year | 4 characters |
Comment | 30 characters |
Genre | 1 byte |
“If you one sum the the size of all these fields we see that 30+30+30+4+30+1 equals 125 bytes and not 128 bytes. The missing three bytes can be found at the very beginning of the tag, before the song title. These three bytes are always “TAG” and is the identification that this is indeed a ID3 tag. The easiest way to find a ID3v1/1.1 tag is to look for the word “TAG” 128 bytes from the end of a file. ” – Quoted from ID3.org
Another pages on MP3 internal:
+ http://www.multiweb.cz/twoinches/mp3inside.htm
+ http://www.fortunecity.com/underworld/sonic/3/id3tag.html
So we can easily extract any MP3 file tag information by parsing its very last 128 bytes (128 bytes at the end).
Once again, I open the song in a hex editor:
That’s right, the info is there!
Generally, instead of reading MP3 file header structure at beginning, we can use the very last 128 bytes to extract its basic info. Interesting, isn’t it?
We’re gonna make a library to query MP3 basic info using Java on Android in next article 🙂
Just take a cup of coffee and enjoy the song “….give me reason …. connect the space between …” , lol :))
Cheers,
Pete Houston