// MP3.java // $Id: MP3File.java,v 1.11 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. /** * Instances of this class contain an MP3 file, giving access to its * ID3 and ID3v2 tags and other mp3 properties. * <p> * It provides a common interface to both tags, e.g. <code>setTitle(title)</code> * updates the title field in both tags. When reading (e.g. <code>getTitle()</code>, * it tries to provide as much information as possible (this means returning the * ID3v2 infos if tag and requested field are present). * <p> * Information stored in tags is always returned as a <code>TagContent</code>, the * description of the respective get Methods state which fields are used. * The more complex frames are not parsed into fields, but rather returned as a * byte array. It is up to the user of this class to make sense of it. Usage of * a special decode class is recommended. * <p> * It is assumed that each ID3v2 frame is unique, as is the case for nearly all * frame types * * @author Jens Vonderheide <jens@vdheide.de> */ package de.vdheide.mp3; import java.io.File; import java.io.IOException; public class MP3File extends java.io.File { public static final String DEF_ASCII_ENCODING = "ISO8859_1"; public static final String UNICODE_ENCODING = "UNICODE"; //"Unicode" /********** Constructors **********/ /** * Creates a new instance. * Tag information is completely read the first time it is requested * and written after <code>update()</code>. * * @param filename File name * @exception IOException If I/O error occurs * @exception NoMP3FrameException If file does not contain at least one mp3 frame * @exception ID3v2WrongCRCException If the ID3v2 tag fails CRC * @exception ID3v2DecompressionException If the ID3v2 tag cannot be decompressed * @exception ID3v2IllegalVersionException If the ID3v2 tag has a wrong (too high) version */ public MP3File(String filename) throws IOException, NoMP3FrameException, ID3v2WrongCRCException, ID3v2DecompressionException, ID3v2IllegalVersionException { super(filename); // read properties and tags reset(null); } /** * Creates a MP3File instance that represents the file with the specified * name in the specified directory. * Tag information is completely read the first time it is requested * and written after <code>update()</code>. * * @param dir Directory * @param filename File name * @exception IOException If I/O error occurs * @exception NoMP3FrameException If file does not contain at least one mp3 frame * @exception ID3v2WrongCRCException If the ID3v2 tag fails CRC * @exception ID3v2DecompressionException If the ID3v2 tag cannot be decompressed * @exception ID3v2IllegalVersionException If the ID3v2 tag has a wrong (too high) version */ public MP3File(File dir, String filename) throws IOException, NoMP3FrameException, ID3v2WrongCRCException, ID3v2DecompressionException, ID3v2IllegalVersionException { super(dir, filename); // read properties and tags reset(null); } /** * Creates a MP3File instance that represents the file with the specified * name in the specified directory. * Tag information is completely read the first time it is requested * and written after <code>update()</code>. * * @param dir Directory * @param filename File name * @param encoding Encoding * @exception IOException If I/O error occurs * @exception NoMP3FrameException If file does not contain at least one mp3 frame * @exception ID3v2WrongCRCException If the ID3v2 tag fails CRC * @exception ID3v2DecompressionException If the ID3v2 tag cannot be decompressed * @exception ID3v2IllegalVersionException If the ID3v2 tag has a wrong (too high) version */ public MP3File(File dir, String filename, String encoding) throws IOException, NoMP3FrameException, ID3v2WrongCRCException, ID3v2DecompressionException, ID3v2IllegalVersionException { super(dir, filename); // read properties and tags reset(encoding); } /** * Creates a File instance whose pathname is the pathname of the specified directory, * followed by the separator character, followed by the name * argument. * Tag information is completely read the first time it is requested * and written after <code>update()</code>. * * @param dir Name of directory * @param filename File name * @exception IOException If I/O error occurs * @exception NoMP3FrameException If file does not contain at least one mp3 frame * @exception ID3v2WrongCRCException If the ID3v2 tag fails CRC * @exception ID3v2DecompressionException If the ID3v2 tag cannot be decompressed * @exception ID3v2IllegalVersionException If the ID3v2 tag has a wrong (too high) version */ public MP3File(String dir, String filename) throws IOException, NoMP3FrameException, ID3v2WrongCRCException, ID3v2DecompressionException, ID3v2IllegalVersionException { super(dir, filename); // read properties and tags reset(null); } /** * Creates a File instance whose pathname is the pathname of the specified directory, * followed by the separator character, followed by the name * argument. * Tag information is completely read the first time it is requested * and written after <code>update()</code>. * * @param dir Name of directory * @param filename File name * @param encoding Encoding * @exception IOException If I/O error occurs * @exception NoMP3FrameException If file does not contain at least one mp3 frame * @exception ID3v2WrongCRCException If the ID3v2 tag fails CRC * @exception ID3v2DecompressionException If the ID3v2 tag cannot be decompressed * @exception ID3v2IllegalVersionException If the ID3v2 tag has a wrong (too high) version */ public MP3File(String dir, String filename, String encoding) throws IOException, NoMP3FrameException, ID3v2WrongCRCException, ID3v2DecompressionException, ID3v2IllegalVersionException { super(dir, filename); // read properties and tags reset(encoding); } /********** Public methods **********/ /** Allows to change encoding, for example read in one encoding, then write in different. * @param encoding */ public void setTagsEncoding(String encoding) { this.encoding = encoding; id3.setEncoding(encoding); } /** just for reading back */ public String getTagsEncoding() { return encoding; } /** * Commit information to file * * @exception ID3Exception If an error occurs when writing the ID3 tag * @exception ID3v2Exception If an error ocurrs when writing the ID3v2 tag */ public void update() throws ID3Exception, ID3v2Exception { // write id3v1 if (write_id3) { try { id3.writeTag(); } catch (IOException e) { throw new ID3Exception(); } } // write id3v2 if (write_id3v2) { try { id3v2.update(); } catch (IOException e) { throw new ID3v2Exception(e.getMessage()); } } // Properties are read only... } /** * Write ID3 tag? * * @param write_id3 True: Write ID3 tag on update */ public void setWriteID3(boolean write_id3) { this.write_id3 = write_id3; } /** * Should an ID3 tag be written * * @return true if ID3 tag will be written on update */ public boolean getWriteID3() { return write_id3; } /** * Write ID3v2 tag? * * @param write_id3v2 True: Write ID3v2 tag on update */ public void setWriteID3v2(boolean write_id3v2) { this.write_id3v2 = write_id3v2; } /** * Should an ID3v2 tag be written? * * @return true if ID3v2 tag will be written on update */ public boolean getWriteID3v2() { return write_id3v2; } /** Is id3 v2 present ? * * @return true when id3 ver 2 has been read */ public boolean isID3v2() { return id3v2.header != null; } /** * Use compression in ID3v2 tag? * Frames are compressed only when the compressed content is smaller * than the uncompressed content. * * @param use_compression True: Use compression */ public void setUseCompression(boolean use_compression) { this.use_compression = use_compression; } /** * @return true if compression will be used in ID3v2 tag */ public boolean getUseCompression() { return use_compression; } /** * Use CRC in ID3v2 tag? * * @param use_crc True: Use CRC */ public void setUseCRC(boolean use_crc) { this.use_crc = use_crc; // inform id3v2 tag if (id3v2 != null) { id3v2.setUseCRC(use_crc); } } /** * @return true if CRC will be used in ID3v2 tag */ public boolean getUseCRC() { return use_crc; } /** * Use padding in ID3v2 tag? * * @param use_padding True: Use padding */ public void setUsePadding(boolean use_padding) { this.use_padding = use_padding; // inform id3v2 tag if (id3v2 != null) { id3v2.setUsePadding(use_padding); } } /** * @return true if padding will be used in ID3v2 tag */ public boolean getUsePadding() { return use_padding; } /** * Use unsynchronization in ID3v2 tag? * * @param use_unsynch True: Use unsynchronization */ public void setUseUnsynchronization(boolean use_unsynch) { this.use_unsynchronization = use_unsynch; // inform id3v2 tag if (id3v2 != null) { id3v2.setUseUnsynchronization(use_unsynch); } } /** * @return true if unsynchronization will be used in ID3v2 tag */ public boolean getUseUnsynchronization() { return use_unsynchronization; } // Read MP3 properties /** * @return MPEG level (1 or 2) */ public int getMPEGLevel() { return prop.getMPEGLevel(); } /** * @return Layer (1..3) */ public int getLayer() { return prop.getLayer(); } /** * @return Bitrate */ public int getBitrate() { return prop.getBitrate(); } /** * @return Samplerate */ public int getSamplerate() { return prop.getSamplerate(); } /** * Returns mode (mono, stereo, etc.) used in MP3 file * Better use constants from MP3Properties. * * @return Mode */ public int getMode() { return prop.getMode(); } /** * Returns emphasis used in MP3 file * Better use constants from MP3Properties. * * @return Emphasis */ public int getEmphasis() { return prop.getEmphasis(); } /** * @return Protection (CRC) set */ public boolean getProtection() { return prop.getProtection(); } /** * @return Private bit set? */ public boolean getPrivate() { return prop.getPrivate(); } /** * @return Padding set? */ public boolean getPadding() { return prop.getPadding(); } /** * @return Copyright set? */ public boolean getCopyright() { return prop.getCopyright(); } /** * @return Original? */ public boolean getOriginal() { return prop.getOriginal(); } /** * @return Length in seconds */ public long getLength() { return prop.getLength(); } public boolean isVBR() { return prop.isVBR(); } // Tag information (for details see the ID3v2 informal standard) /** * Read album/movie/show title. Album is stored as text content. * * @return Album * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getAlbum() throws FrameDamagedException { TagContent ret = TextFrameEncoding.read(id3v2, id3v2.getFrameCode(ID3v2.ALBUM)); if (ret.getTextContent() == null) { try { ret.setContent(id3.getAlbum()); } catch (NoID3TagException e) { // do nothing, content just stays at null } } return ret; } /** * Set album. Album is read from text content. * * @param album Album to set * @exception TagFormatException If information to set is not correctly formatted */ public void setAlbum(TagContent album) throws TagFormatException { if (album.getTextContent()==null) { throw new TagFormatException(); } // write v1 id3.setAlbum(album.getTextContent()); // write v2 (new TextFrameEncoding(id3v2, "TALB", album, use_compression)).write(); } /** * Read BPM. BPM is stored as text content. * * @return BPM * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getBPM() throws FrameDamagedException { return TextFrameEncoding.read(id3v2, id3v2.getFrameCode(ID3v2.BPM)); } /** * Set BPM. BPM is read from text content. * * @param bpm BPM to set * @exception TagFormatException If information to set is not correctly formatted */ public void setBPM(TagContent bpm) throws TagFormatException { if (bpm.getTextContent()==null || checkNumeric(bpm.getTextContent()) == false) { throw new TagFormatException(); } (new TextFrameEncoding(id3v2, "TBPM", bpm, use_compression)).write(); } /** * Read composer(s), stored as text content. * * @return composer(s) * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getComposer() throws FrameDamagedException { return TextFrameEncoding.read(id3v2, id3v2.getFrameCode(ID3v2.COMPOSER)); } /** * Set composer(s), read from text content. * * @param composer Composer(s) to set * @exception TagFormatException If information to set is not correctly formatted */ public void setComposer(TagContent composer) throws TagFormatException { (new TextFrameEncoding(id3v2, "TCOM", composer, use_compression)).write(); } /** * Read genre (type of music like "Soul", "Rock", etc.) stored as text content. * ID3v1.1 content is denoted by putting round brackets around the number (like (4)), * round brackets in text are escaped by ((. * * @return Album * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getGenre() throws FrameDamagedException { TagContent ret = new TagContent(); byte []v2cont = null; try { v2cont = ((ID3v2Frame)(id3v2.getFrame(id3v2.getFrameCode(ID3v2.GENRE)).elementAt(0))).getContent(); } catch (ID3v2Exception e) {} if (v2cont == null) { // try id3v1 int v1cont; try { v1cont = id3.getGenre(); // convert id3v1 info to new format ret.setContent( "("+String.valueOf(v1cont)+')' ); } catch (ID3Exception e) { // no info } } else { // use v2 Parser parse = new Parser(v2cont, true, encoding); try { ret.setContent(parse.parseText()); } catch (ParseException e) { throw new FrameDamagedException(); } } return ret; } /** * Set genre, read from text content. * ID3v1.1 genre is denoted by putting round brackets around the number (like (4)), * round brackets in text are escaped by ((. * * @param genre Genre to set * @exception TagFormatException If information to set is not correctly formatted */ public void setGenre(TagContent genre) throws TagFormatException { if (genre.getTextContent() == null) { throw new TagFormatException(); } // write v1 // parse genre String sGenre = genre.getTextContent(); int brackpos = sGenre.indexOf('('); boolean suc = false; if (brackpos >= 0) { int ebrackpos = sGenre.indexOf(')', brackpos); if (ebrackpos > brackpos) try { id3.setGenre(Integer.parseInt(sGenre.substring(brackpos+1, ebrackpos))); suc = true; } catch (ID3IllegalFormatException e2) { } catch (Exception e2) { } } else { try { id3.setGenre(Integer.parseInt(sGenre)); suc = true; } catch (ID3IllegalFormatException e2) { } catch (Exception e2) { } } if (suc == false) { // could not parse a genre number try { id3.setGenre(12); // Genre: OTHER } catch (ID3IllegalFormatException e2) { } } ByteBuilder build = new ByteBuilder(TextFrame.UNICODE); build.put(sGenre); byte []v2cont = build.getBytes(); //// store // remove frame try { id3v2.removeFrame("TCON"); } catch (ID3v2Exception e) {} // store frame try { ID3v2Frame add = new ID3v2Frame("TCON", v2cont, false, false, false, (use_compression ? ID3v2Frame.DO_COMPRESS : ID3v2Frame.NO_COMPRESSION), (byte)0, (byte)0); id3v2.addFrame(add); } catch (ID3v2DecompressionException e) {} } /** * Read copyright, store as text content. According to the ID3v2 informal standard, * this has to be preceded when displayed with "Copyright (C)" where (C) is one * character showing a C in a circle... * * @return Copyright * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getCopyrightText() throws FrameDamagedException { return TextFrameEncoding.read(id3v2, "TCOP"); } /** * Set copyright, read from text content. * * @param copyright Copyright to set * @exception TagFormatException If information to set is not correctly formatted */ public void setCopyrightText(TagContent copyright) throws TagFormatException { (new TextFrameEncoding(id3v2, "TCOP", copyright, use_compression)).write(); } /** * Read date (format DDMM), store as text content. * * @return date * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getDate() throws FrameDamagedException { return TextFrameEncoding.read(id3v2, "TDAT"); } /** * Set date (format DDMM), read from text content. * * @param date Date to set * @exception TagFormatException If input does not adhere to the format given above. */ public void setDate(TagContent date) throws TagFormatException { // check format if (date.getTextContent() == null || checkExactLength(date.getTextContent(), 4) == false || checkNumeric(date.getTextContent()) == false) { throw new TagFormatException(); } (new TextFrameEncoding(id3v2, "TDAT", date, use_compression)).write(); } /** * Read playlist delay, store as text content. * * @return Playlist delay * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getPlaylistDelay() throws FrameDamagedException { return TextFrameEncoding.read(id3v2, "TDLY"); } /** * Set playlist delay, read from text content. * * @param delay Playlist delay to set * @exception TagFormatException If input is not a numeric string */ public void setPlaylistDelay(TagContent delay) throws TagFormatException { // check format if (delay.getTextContent() == null || !checkNumeric(delay.getTextContent())) { throw new TagFormatException(); } (new TextFrameEncoding(id3v2, "TDLY", delay, use_compression)).write(); } /** * Read encoded by, store as text content. * * @return Encoded by * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getEncodedBy() throws FrameDamagedException { return TextFrameEncoding.read(id3v2, "TENC"); } /** * Set encoded by, read from text content. * * @param encoder Encoded by to set * @exception TagFormatException If information to set is not correctly formatted */ public void setEncodedBy(TagContent encoder) throws TagFormatException { (new TextFrameEncoding(id3v2, "TENC", encoder, use_compression)).write(); } /** * Read lyricist, store as text content. * * @return Lyricist * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getLyricist() throws FrameDamagedException { return TextFrameEncoding.read(id3v2, "TEXT"); } /** * Set lyricist, read from text content. * * @param lyricist Lyricist to set * @exception TagFormatException If information to set is not correctly formatted */ public void setLyricist(TagContent lyricist) throws TagFormatException { (new TextFrameEncoding(id3v2, "TEXT", lyricist, use_compression)).write(); } /** * Read file type, store as text content. * <p> * The following types are defined (other types may be used) * <ul> * <li><code>MPG </code>: MPEG Audio * <li><code>MPG/1 </code>: MPEG 1/2 layer I</li> * <li><code>MPG/2 </code>: MPEG 1/2 layer II</li> * <li><code>MPG/3 </code>: MPEG 1/2 layer III</li> * <li><code>MPG/2.5</code>: MPEG 2.5</li> * <li><code>MPG/AAC</code>: Advanced audio compression</li> * <li><code>VQF </code>: Transform-domain weighted interleace vector quantization</li> * <li><code>PCM </code>: Pulse code modulated audio</li> * </ul> * <p> * * @return File type * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getFileType() throws FrameDamagedException { return TextFrameEncoding.read(id3v2, "TFLT"); } /** * Set file type, read from text content. * * @param type File type to set * @exception TagFormatException If information to set is not correctly formatted */ public void setFileType(TagContent type) throws TagFormatException { (new TextFrameEncoding(id3v2, "TFLT", type, use_compression)).write(); } /** * Read time (format HHMM), store as text content. * * @return Time * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getTime() throws FrameDamagedException { return TextFrameEncoding.read(id3v2, "TIME"); } /** * Set time (format HHMM), read from text content. * * @param time Time to set * @exception TagFormatException If input does not adhere to the format given above. */ public void setTime(TagContent time) throws TagFormatException { // check format if (time.getTextContent() == null || !checkExactLength(time.getTextContent(), 4) || !checkNumeric(time.getTextContent())) { throw new TagFormatException(); } (new TextFrameEncoding(id3v2, "TIME", time, use_compression)).write(); } /** * Read content group description, store as text content. * <p> * Content group description is used if sound belongs to a larger * category of sounds, e.g. "Piano Concerto", "Weather - Hurricane") * * @return Content group * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getContentGroup() throws FrameDamagedException { return TextFrameEncoding.read(id3v2, "TIT1"); } /** * Set content group description, read from text content. * <p> * Content group description is used if sound belongs to a larger * category of sounds, e.g. "Piano Concerto", "Weather - Hurricane") * * @param content Content group description to set * @exception TagFormatException If information to set is not correctly formatted */ public void setContentGroup(TagContent content) throws TagFormatException { (new TextFrameEncoding(id3v2, "TIT1", content, use_compression)).write(); } /** * Read song title, store as text content. * * @return Song title * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getTitle() throws FrameDamagedException { TagContent ret = TextFrameEncoding.read(id3v2, id3v2.getFrameCode(ID3v2.TITLE)); if (ret.getTextContent() == null) { // try id3v1 try { ret.setContent(id3.getTitle()); } catch (NoID3TagException e) { } } return ret; } /** * Set title, read from text content. * * @param title Title to set * @exception TagFormatException If information to set is not correctly formatted */ public void setTitle(TagContent title) throws TagFormatException { if (title.getTextContent() == null) { throw new TagFormatException(); } // write v1 id3.setTitle(title.getTextContent()); (new TextFrameEncoding(id3v2, "TIT2", title, use_compression)).write(); } /** * Read subtitle, store as text content. * <p> * Subtitle is used for information directly related to the contents title * (e.g. "Op. 16" or "Performed live at Wembley") * * @return Subtitle * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getSubtitle() throws FrameDamagedException { return TextFrameEncoding.read(id3v2, "TIT3"); } /** * Set subtitle, read from text content. * <p> * Content group description is used if sound belongs to a larger * category of sounds, e.g. "Piano Concerto", "Weather - Hurricane") * * @param subtitle Subtitle to set * @exception TagFormatException If information to set is not correctly formatted */ public void setSubtitle(TagContent subtitle) throws TagFormatException { (new TextFrameEncoding(id3v2, "TIT3", subtitle, use_compression)).write(); } /** * Read initial key * <p> * Musical key in which sound starts. String with max 3 characters, ground keys: * A, B, C, D, E, F, G, halfkeys b and #. Minor: m, Off key: o * * @return Initial key * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getInitialKey() throws FrameDamagedException { return TextFrameEncoding.read(id3v2, "TKEY"); } /** * Set initial key, read from text content. * <p> * Musical key in which sound starts. String with max 3 characters, ground keys: * A, B, C, D, E, F, G, halfkeys b and #. Minor: m, Off key: o * * @param key Initial key to set * @exception TagFormatException If key is longer than three characters */ public void setInitialKey(TagContent key) throws TagFormatException { if (key.getTextContent() == null || !checkMaxLength(key.getTextContent(), 3)) { throw new TagFormatException(); } (new TextFrameEncoding(id3v2, "TKEY", key, use_compression)).write(); } /** * Read language of lyrics * <p> * Language is represented with three characters according to ISO-639-2. * * @return Language * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getLanguage() throws FrameDamagedException { return TextFrameEncoding.read(id3v2, "TLAN"); } /** * Set language of lyrics, read from text content. * <p> * Language is represented with three characters according to ISO-639-2. * * @param lang Language to set * @exception TagFormatException If information to set is not correctly formatted */ public void setLanguage(TagContent lang) throws TagFormatException { if (lang.getTextContent() == null || !checkExactLength(lang.getTextContent(), 3)) { throw new TagFormatException(); } (new TextFrameEncoding(id3v2, "TLAN", lang, use_compression)).write(); } /** * Read length of audiofile in milliseconds, store as text content. * <p> * This returns the length stored in the ID3v2 tag, not the length calculated from * file length. * * @return Length * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getLengthInTag() throws FrameDamagedException { return TextFrameEncoding.read(id3v2, "TLEN"); } /** * Set length of audiofile in milliseconds, read from text content. * * @param length Length to set * @exception TagFormatException If input is not a numeric string */ public void setLengthInTag(TagContent length) throws TagFormatException { // check format if (length.getTextContent() == null || !checkNumeric(length.getTextContent())) { throw new TagFormatException(); } (new TextFrameEncoding(id3v2, "TLEN", length, use_compression)).write(); } /** * Read media type, store as text content. * <p> * See the ID3v2 informal standard for more information. * * @return Media type * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getMediaType() throws FrameDamagedException { return TextFrameEncoding.read(id3v2, "TMED"); } /** * Set media type, read from text content. * <p> * See the ID3v2 informal standard for more information. * * @param type Media type to set * @exception TagFormatException If information to set is not correctly formatted */ public void setMediaType(TagContent type) throws TagFormatException { (new TextFrameEncoding(id3v2, "TMED", type, use_compression)).write(); } /** * Read original title (for cover songs), store as text content * * @return Original title * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getOriginalTitle() throws FrameDamagedException { return TextFrameEncoding.read(id3v2, "TOAL"); } /** * Set original title, read from text content. * * @param title Original title to set * @exception TagFormatException If information to set is not correctly formatted */ public void setOriginalTitle(TagContent title) throws TagFormatException { (new TextFrameEncoding(id3v2, "TOAL", title, use_compression)).write(); } /** * Read original filename, store as text content * <p> * Original filename is used to store prefered filename on media which does have limitations * to the filename. It is stored including suffix. * * @return Original filename * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getOriginalFilename() throws FrameDamagedException { return TextFrameEncoding.read(id3v2, "TOFN"); } /** * Set original filename, read from text content. * <p> * Original filename is used to store prefered filename on media which have limitations * to the filename. It is stored including suffix. * * @param filename Original filename to set * @exception TagFormatException If information to set is not correctly formatted */ public void setOriginalFilename(TagContent filename) throws TagFormatException { (new TextFrameEncoding(id3v2, "TOFN", filename, use_compression)).write(); } /** * Read original lyricist(s) (for cover songs), store as text content * * @return Original lyricist(s) * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getOriginalLyricist() throws FrameDamagedException { return TextFrameEncoding.read(id3v2, "TOLY"); } /** * Set original lyricist(s), read from text content. * * @param lyricist Original lyricist(s) to set * @exception TagFormatException If information to set is not correctly formatted */ public void setOriginalLyricist(TagContent lyricist) throws TagFormatException { (new TextFrameEncoding(id3v2, "TOLY", lyricist, use_compression)).write(); } /** * Read original artist(s) (for cover songs), store as text content * * @return Original artist(s) * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getOriginalArtist() throws FrameDamagedException { return TextFrameEncoding.read(id3v2, "TOPE"); } /** * Set original artist(s), read from text content. * * @param artist Original artist(s) to set * @exception TagFormatException If information to set is not correctly formatted */ public void setOriginalArtist(TagContent artist) throws TagFormatException { (new TextFrameEncoding(id3v2, "TOPE", artist, use_compression)).write(); } /** * Read original release year (format YYYY) (for cover songs), store as text content * * @return Original release year * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getOriginalYear() throws FrameDamagedException { return TextFrameEncoding.read(id3v2, "TORY"); } /** * Set original release year (format YYYY), read from text content. * * @param year Original year to set * @exception TagFormatException If input is not in the format listed above */ public void setOriginalYear(TagContent year) throws TagFormatException { // check format if (year.getTextContent() == null || !checkExactLength(year.getTextContent(), 4) || !checkNumeric(year.getTextContent())) { throw new TagFormatException(); } (new TextFrameEncoding(id3v2, "TORY", year, use_compression)).write(); } /** * Read file owner, store as text content * * @return File owner * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getFileOwner() throws FrameDamagedException { return TextFrameEncoding.read(id3v2, "TOWN"); } /** * Set file owner, read from text content. * * @param owner File owner to set * @exception TagFormatException If information to set is not correctly formatted */ public void setFileOwner(TagContent owner) throws TagFormatException { (new TextFrameEncoding(id3v2, "TOWN", owner, use_compression)).write(); } /** * Read artist, store as text content. * * @return Artist * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getArtist() throws FrameDamagedException { TagContent ret = TextFrameEncoding.read(id3v2, id3v2.getFrameCode(ID3v2.ARTIST)); if (ret.getTextContent() == null) { try { ret.setContent(id3.getArtist()); } catch (NoID3TagException e) { } } return ret; } /** * Set artist, read from text content. * * @param artist Artist to set * @exception TagFormatException If information to set is not correctly formatted */ public void setArtist(TagContent artist) throws TagFormatException { // write v1 id3.setArtist(artist.getTextContent()); (new TextFrameEncoding(id3v2, "TPE1", artist, use_compression)).write(); } /** * Read band (orchestra, accompaniment), store as text content * * @return Band * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getBand() throws FrameDamagedException { return TextFrameEncoding.read(id3v2, id3v2.getFrameCode(ID3v2.BAND)); } /** * Set band, read from text content. * * @param band Band to set * @exception TagFormatException If information to set is not correctly formatted */ public void setBand(TagContent band) throws TagFormatException { (new TextFrameEncoding(id3v2, "TPE2", band, use_compression)).write(); } /** * Read conductor, store as text content * * @return Conductor * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getConductor() throws FrameDamagedException { return TextFrameEncoding.read(id3v2, id3v2.getFrameCode(ID3v2.CONDUCTOR)); } /** * Set conductor, read from text content. * * @param conductor Conductor to set * @exception TagFormatException If information to set is not correctly formatted */ public void setConductor(TagContent conductor) throws TagFormatException { (new TextFrameEncoding(id3v2, "TPE3", conductor, use_compression)).write(); } /** * Read remixer, store as text content * * @return Remixer * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getRemixer() throws FrameDamagedException { return TextFrameEncoding.read(id3v2, id3v2.getFrameCode(ID3v2.REMIXER)); } /** * Set remixer, read from text content. * * @param conductor Remixer to set * @exception TagFormatException If information to set is not correctly formatted */ public void setRemixer(TagContent remixer) throws TagFormatException { (new TextFrameEncoding(id3v2, "TPE4", remixer, use_compression)).write(); } /** * Read part of a set (e.g. 1/2 for a double CD), store as text content * * @return Part of a set * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getPartOfSet() throws FrameDamagedException { return TextFrameEncoding.read(id3v2, id3v2.getFrameCode(ID3v2.PARTOFSET)); } /** * Set part of a set (e.g. 1/2 for a double CD), read from text content. * * @param part Part of a set to set * @exception TagFormatException If information to set is not correctly formatted */ public void setPartOfSet(TagContent part) throws TagFormatException { (new TextFrameEncoding(id3v2, "TPOS", part, use_compression)).write(); } /** * Read publisher, store as text content * * @return Publisher * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getPublisher() throws FrameDamagedException { return TextFrameEncoding.read(id3v2, id3v2.getFrameCode(ID3v2.PUBLISHER)); } /** * Set publisher, read from text content. * * @param publisher Publisher to set * @exception TagFormatException If information to set is not correctly formatted */ public void setPublisher(TagContent publisher) throws TagFormatException { (new TextFrameEncoding(id3v2, "TPUB", publisher, use_compression)).write(); } /** * Read track number, store in text content. * * @return Track number * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getTrack() throws FrameDamagedException { TagContent ret = new TagContent(); byte []v2cont = null; try { v2cont = ((ID3v2Frame)(id3v2.getFrame(id3v2.getFrameCode(ID3v2.TRACK)).elementAt(0))).getContent(); } catch (ID3v2Exception e) { // no info, wait for ID3 } if (v2cont == null) { // try id3v1 String v1cont; try { v1cont = String.valueOf(id3.getTrack()); ret.setContent(v1cont); } catch (ID3Exception e) { // no info } } else { // use v2 Parser parse = new Parser(v2cont, true, encoding); try { ret.setContent(parse.parseText()); } catch (ParseException e) { throw new FrameDamagedException(); } } return ret; } /** * Set track number, read from text content. * * @param track Track number to set * @exception TagFormatException If information to set is not correctly formatted */ public void setTrack(TagContent track) throws TagFormatException { if (track.getTextContent() == null) { throw new TagFormatException(); } // write v1 // parse track boolean found = false; int slashpos = track.getTextContent().indexOf('/'); String trackstring; if (slashpos != -1) { // using notation n/x trackstring = track.getTextContent().substring(0, slashpos); } else { trackstring = track.getTextContent(); } try { Integer test = new Integer(trackstring); try { id3.setGenre(test.intValue()); } catch (Exception e) {} } catch (NumberFormatException e) {} ByteBuilder build = new ByteBuilder(TextFrame.UNICODE); build.put(track.getTextContent()); byte []v2cont = build.getBytes(); //// store // remove frame try { id3v2.removeFrame("TRCK"); } catch (ID3v2Exception e) { } // store frame try { ID3v2Frame add = new ID3v2Frame("TRCK", v2cont, false, false, false, (use_compression ? ID3v2Frame.DO_COMPRESS : ID3v2Frame.NO_COMPRESSION), (byte)0, (byte)0); id3v2.addFrame(add); } catch (ID3v2DecompressionException e) {} } /** Get compilation flag * * @return Compilation flag * @exception */ public TagContent getCompilation() throws FrameDamagedException { TagContent ret = new TagContent(); byte []v2cont = null; try { v2cont = ((ID3v2Frame)(id3v2.getFrame(id3v2.getFrameCode(ID3v2.COMPILATION)).elementAt(0))).getContent(); } catch (ID3v2Exception e) { // no info, wait for ID3 } if (v2cont != null) { // use v2 Parser parse = new Parser(v2cont, true, encoding); try { ret.setContent(parse.parseText()); } catch (ParseException e) { throw new FrameDamagedException(); } } return ret; } public void setCompilation(TagContent compilation) throws TagFormatException { (new TextFrameEncoding(id3v2, "TCMP", compilation, use_compression)).write(); } /** * Get recording dates, store as text content * * @return Recording dates * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getRecordingDates() throws FrameDamagedException { return TextFrameEncoding.read(id3v2, "TRDA"); } /** * Set recording date, read from text content. * * @param date Recording date * @exception TagFormatException If information to set is not correctly formatted */ public void setRecordingDate(TagContent date) throws TagFormatException { (new TextFrameEncoding(id3v2, "TRDA", date, use_compression)).write(); } /** * Get Internet radio station name, store as text content * * @return Internet radio station name * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getInternetRadioStationName() throws FrameDamagedException { return TextFrameEncoding.read(id3v2, "TRSN"); } /** * Set Internet radio station name, read from text content. * * @param name Internet radio station name * @exception TagFormatException If information to set is not correctly formatted */ public void setInternetRadioStationName(TagContent name) throws TagFormatException { (new TextFrameEncoding(id3v2, "TRSO", name, use_compression)).write(); } /** * Get Internet radio station owner, store as text content * * @return Internet radio station owner * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getInternetRadioStationOwner() throws FrameDamagedException { return TextFrameEncoding.read(id3v2, "TRSO"); } /** * Set Internet radio station owner, read from text content. * * @param owner Station owner * @exception TagFormatException If information to set is not correctly formatted */ public void setInternetRadioStationOwner(TagContent owner) throws TagFormatException { (new TextFrameEncoding(id3v2, "TRSO", owner, use_compression)).write(); } /** * Get size of file in bytes, excluding id3v2 tag, store as text content * * @return Size of File * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getFilesize() throws FrameDamagedException { return TextFrameEncoding.read(id3v2, "TSIZ"); } /** * Set size of files in bytes, excluding id3v2 tag, read from text content. * * @param size Size of file * @exception TagFormatException If input is not numeric */ public void setFilesize(TagContent size) throws TagFormatException { if (size.getTextContent() == null || !checkNumeric(size.getTextContent())) { throw new TagFormatException(); } (new TextFrameEncoding(id3v2, "TSIZ", size, use_compression)).write(); } /** * Get International Standard Recording Code, store as text content * * @return ISRC * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getISRC() throws FrameDamagedException { return TextFrameEncoding.read(id3v2, "TSRC"); } /** * Set International Standard Recording Code, read from text content. * * @param isrc ISRC * @exception TagFormatException If input is not of 12 character's length */ public void setISRC(TagContent isrc) throws TagFormatException { (new TextFrameEncoding(id3v2, "TSRC", isrc, use_compression)).write(); } /** * Get year of recording, store as text content * * @return Recording dates * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getYear() throws FrameDamagedException { TagContent ret = TextFrameEncoding.read(id3v2, id3v2.getFrameCode(ID3v2.YEAR)); if (ret.getTextContent() == null) { try { ret.setContent(id3.getYear()); } catch (NoID3TagException e) { } } return ret; } /** * Set year of recording, read from text content. * * @param year Year of recording * @exception TagFormatException If input is not numeric or not 4 or 5 characters */ public void setYear(TagContent year) throws TagFormatException { if (year.getTextContent() == null || !checkNumeric(year.getTextContent()) || (!checkMaxLength(year.getTextContent(), 4) && !checkMaxLength(year.getTextContent(), 5))) { throw new TagFormatException(); } id3.setYear(year.getTextContent()); (new TextFrameEncoding(id3v2, "TYER", year, use_compression)).write(); } ////// URL link frames /** * Read Commercial information webpage, store as text content * * @return Commercial information * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getCommercialInformation() throws FrameDamagedException { return TextFrameNoEncoding.read(id3v2, "WCOM"); } /** * Set Commercial information webpage, read from text content. * * @param info Commercial information to set * @exception TagFormatException If information to set is not correctly formatted */ public void setCommercialInformation(TagContent info) throws TagFormatException { (new TextFrameNoEncoding(id3v2, "WCOM", info, use_compression)).write(); } /** * Read Coypright / legal information webpage, store as text content * * @return Copyright webpage * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getCopyrightWebpage() throws FrameDamagedException { return TextFrameNoEncoding.read(id3v2, "WCOP"); } /** * Set Copyright / legal information webpage, read from text content. * * @param copy Copyright webpage to set * @exception TagFormatException If information to set is not correctly formatted */ public void setCopyrightWebpage(TagContent copy) throws TagFormatException { (new TextFrameNoEncoding(id3v2, "WCOP", copy, use_compression)).write(); } /** * Read official audio file webpage, store as text content * * @return Audio file webpage * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getAudioFileWebpage() throws FrameDamagedException { return TextFrameNoEncoding.read(id3v2, "WOAF"); } /** * Set official audio file webpage, read from text content. * * @param page Official audio file webpage to set * @exception TagFormatException If information to set is not correctly formatted */ public void setAudioFileWebpage(TagContent page) throws TagFormatException { (new TextFrameNoEncoding(id3v2, "WOAF", page, use_compression)).write(); } /** * Read official artist / performer webpage, store as text content * * @return Artist webpage * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getArtistWebpage() throws FrameDamagedException { return TextFrameNoEncoding.read(id3v2, "WOAR"); } /** * Set official artist / performer webpage, read from text content. * * @param page Artist webpage webpage to set * @exception TagFormatException If information to set is not correctly formatted */ public void setArtistWebpage(TagContent page) throws TagFormatException { (new TextFrameNoEncoding(id3v2, "WOAR", page, use_compression)).write(); } /** * Read official audio source webpage, store as text content * Used e.g. for movie soundtracks, then points to the movie * * @return Audio source webpage * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getAudioSourceWebpage() throws FrameDamagedException { return TextFrameNoEncoding.read(id3v2, "WOAS"); } /** * Set official audio source webpage, read from text content. * * @param page Official audio source webpage to set * @exception TagFormatException If information to set is not correctly formatted */ public void setAudioSourceWebpage(TagContent page) throws TagFormatException { (new TextFrameNoEncoding(id3v2, "WOAS", page, use_compression)).write(); } /** * Read official internet radio station webpage, store as text content * * @return Internet radio station webpage * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getInternetRadioStationWebpage() throws FrameDamagedException { return TextFrameNoEncoding.read(id3v2, "WORS"); } /** * Set official internet radio station webpage, read from text content. * * @param page Official internet radio station webpage to set * @exception TagFormatException If information to set is not correctly formatted */ public void setInternetRadioStationWebpage(TagContent page) throws TagFormatException { (new TextFrameNoEncoding(id3v2, "WORS", page, use_compression)).write(); } /** * Read payment webpage, store as text content * * @return Payment webpage * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getPaymentWebpage() throws FrameDamagedException { return TextFrameNoEncoding.read(id3v2, "WPAY"); } /** * Set payment webpage, read from text content. * * @param page Payment webpage to set * @exception TagFormatException If information to set is not correctly formatted */ public void setPaymentWebpage(TagContent page) throws TagFormatException { (new TextFrameNoEncoding(id3v2, "WPAY", page, use_compression)).write(); } /** * Read official publishers webpage, store as text content * * @return Publishers webpage * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getPublishersWebpage() throws FrameDamagedException { return TextFrameNoEncoding.read(id3v2, "WPUB"); } /** * Set official publishers webpage, read from text content. * * @param page Official publishers webpage to set * @exception TagFormatException If information to set is not correctly formatted */ public void setPublishersWebpage(TagContent page) throws TagFormatException { (new TextFrameNoEncoding(id3v2, "WPUB", page, use_compression)).write(); } ////// Binary frames /** * Read event timing codes, store as binary content * See the ID3v2 informal standard for details on the format of this field. * * @return Event timing codes * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getEventTimingCodes() throws FrameDamagedException { return BinaryFrame.read(id3v2, "ETCO"); } /** * Set event timing codes, read from binary content. * * @param codes Timing codes to set * @exception TagFormatException If information to set is not correctly formatted */ public void setEventTimingCodes(TagContent codes) throws TagFormatException { (new BinaryFrame(id3v2, "ETCO", codes, use_compression)).write(); } /** * Read MPEG location lookup table, store as binary content * See the ID3v2 informal standard for details on the format of this field. * * @return Lookup table * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getLookupTable() throws FrameDamagedException { return BinaryFrame.read(id3v2, "MLLT"); } /** * Set MPEG location lookup table, read from binary content. * See the ID3v2 informal standard for details on the format of this field. * * @param table Location lookup table to set * @exception TagFormatException If information to set is not correctly formatted */ public void setLookupTable(TagContent table) throws TagFormatException { (new BinaryFrame(id3v2, "MLLT", table, use_compression)).write(); } /** * Read synchronized tempo codes, store as binary content * See the ID3v2 informal standard for details on the format of this field. * * @return Synchronized tempo codes * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getSynchronizedTempoCodes() throws FrameDamagedException { return BinaryFrame.read(id3v2, "SYTC"); } /** * Set synchronized tempo codes, read from binary content. * See the ID3v2 informal standard for details on the format of this field. * * @param codes Synchronized tempo codes to set * @exception TagFormatException If information to set is not correctly formatted */ public void setSynchronizedTempoCodes(TagContent codes) throws TagFormatException { (new BinaryFrame(id3v2, "SYTC", codes, use_compression)).write(); } /** * Read synchronized lyrics, store as binary content * See the ID3v2 informal standard for details on the format of this field. * * @return Synchronized lyrics * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getSynchronizedLyrics() throws FrameDamagedException { return BinaryFrame.read(id3v2, "SYLT"); } /** * Set synchronized lyrics, read from binary content. * See the ID3v2 informal standard for details on the format of this field. * * @param lyrics Synchronized lyrics * @exception TagFormatException If information to set is not correctly formatted */ public void setSynchronizedLyrics(TagContent lyrics) throws TagFormatException { (new BinaryFrame(id3v2, "SYLT", lyrics, use_compression)).write(); } /** * Read relative volume adjustment, store as binary content * See the ID3v2 informal standard for details on the format of this field. * * @return Relative volume adjustment * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getRelativeVolumenAdjustment() throws FrameDamagedException { return BinaryFrame.read(id3v2, "RVAD"); } /** * Set relative volume adjustment, read from binary content. * See the ID3v2 informal standard for details on the format of this field. * * @param adjust Relative volume adjustment to set * @exception TagFormatException If information to set is not correctly formatted */ public void setRelativeVolumeAdjustment(TagContent adjust) throws TagFormatException { (new BinaryFrame(id3v2, "RVAD", adjust, use_compression)).write(); } /** * Read equalisation, store as binary content * See the ID3v2 informal standard for details on the format of this field. * * @return Equalisation * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getEqualisation() throws FrameDamagedException { return BinaryFrame.read(id3v2, "EQUA"); } /** * Set equalisation, read from binary content. * See the ID3v2 informal standard for details on the format of this field. * * @param equal Equalisation * @exception TagFormatException If information to set is not correctly formatted */ public void setEqualisation(TagContent equal) throws TagFormatException { (new BinaryFrame(id3v2, "EQUA", equal, use_compression)).write(); } /** * Read reverb, store as binary content * See the ID3v2 informal standard for details on the format of this field. * * @return Reverb * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getReverb() throws FrameDamagedException { return BinaryFrame.read(id3v2, "RVRB"); } /** * Set reverb, read from binary content. * See the ID3v2 informal standard for details on the format of this field. * * @param reverb Reverb * @exception TagFormatException If information to set is not correctly formatted */ public void setReverb(TagContent reverb) throws TagFormatException { (new BinaryFrame(id3v2, "RVRB", reverb, use_compression)).write(); } /** * Read play counter, store as binary content * See the ID3v2 informal standard for details on the format of this field. * * @return Play counter * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getPlayCounter() throws FrameDamagedException { return BinaryFrame.read(id3v2, "PCNT"); } /** * Set play counter, read from binary content. * See the ID3v2 informal standard for details on the format of this field. * * @param count Play counter * @exception TagFormatException If information to set is not correctly formatted */ public void setPlayCounter(TagContent count) throws TagFormatException { (new BinaryFrame(id3v2, "PCNT", count, use_compression)).write(); } /** * Read popularimeter, store as binary content * See the ID3v2 informal standard for details on the format of this field. * * @return Popularimeter * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getPopularimeter() throws FrameDamagedException { return BinaryFrame.read(id3v2, "POPM"); } /** * Set popularimeter, read from binary content. * See the ID3v2 informal standard for details on the format of this field. * * @param pop Popularimeter * @exception TagFormatException If information to set is not correctly formatted */ public void setPopularimeter(TagContent pop) throws TagFormatException { (new BinaryFrame(id3v2, "POPM", pop, use_compression)).write(); } /** * Read recommended buffer size, store as binary content * See the ID3v2 informal standard for details on the format of this field. * * @return Recommended buffer size * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getRecommendedBufferSize() throws FrameDamagedException { return BinaryFrame.read(id3v2, "RBUF"); } /** * Set recommended buffer size, read from binary content. * See the ID3v2 informal standard for details on the format of this field. * * @param size Recommended buffer size * @exception TagFormatException If information to set is not correctly formatted */ public void setRecommendedBufferSize(TagContent size) throws TagFormatException { (new BinaryFrame(id3v2, "RBUF", size, use_compression)).write(); } /** * Read position synchronization, store as binary content * See the ID3v2 informal standard for details on the format of this field. * * @return Position synchronization * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getPositionSynchronization() throws FrameDamagedException { return BinaryFrame.read(id3v2, "POSS"); } /** * Set position synchronization, read from binary content. * See the ID3v2 informal standard for details on the format of this field. * * @param synch Position synchronization * @exception TagFormatException If information to set is not correctly formatted */ public void setPositionSynchronization(TagContent synch) throws TagFormatException { (new BinaryFrame(id3v2, "POSS", synch, use_compression)).write(); } /** * Read ownership, store as binary content * See the ID3v2 informal standard for details on the format of this field. * * @return Ownership * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getOwnership() throws FrameDamagedException { return BinaryFrame.read(id3v2, "OWNE"); } /** * Set ownership, read from binary content. * See the ID3v2 informal standard for details on the format of this field. * * @param owner Ownership * @exception TagFormatException If information to set is not correctly formatted */ public void setOwnership(TagContent owner) throws TagFormatException { (new BinaryFrame(id3v2, "OWNE", owner, use_compression)).write(); } /** * Read commercial frame, store as binary content * See the ID3v2 informal standard for details on the format of this field. * * @return Commercial frame * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getCommercial() throws FrameDamagedException { return BinaryFrame.read(id3v2, "COMR"); } /** * Set commercial frame, read from binary content. * See the ID3v2 informal standard for details on the format of this field. * * @param commercial Commercial frame * @exception TagFormatException If information to set is not correctly formatted */ public void setCommercial(TagContent commercial) throws TagFormatException { (new BinaryFrame(id3v2, "COMR", commercial, use_compression)).write(); } /** * Read Music CD identifier, store as binary content * * @return Music CD identifier * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getCDIdentifier() throws FrameDamagedException { return BinaryFrame.read(id3v2, "MCDI"); } /** * Set music CD identifier, read from binary content. * * @param ident CD identifier * @exception TagFormatException If information to set is not correctly formatted */ public void setCDIdentifier(TagContent ident) throws TagFormatException { (new BinaryFrame(id3v2, "MCDI", ident, use_compression)).write(); } //////// Other frames, each is different to parse /** * Read unique file identifier. Owner identifier is stored as description, * identifier as binary content. * * @return Unique file identifier * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getUniqueFileIdentifier() throws FrameDamagedException { byte []v2cont = Frame.read(id3v2, "UFID"); if (v2cont == null) { return new TagContent(); } else { TagContent ret = new TagContent(); Parser parse = new Parser(v2cont, false); try { ret.setDescription(parse.parseText()); ret.setContent(parse.parseBinary()); return ret; } catch (ParseException e) { throw new FrameDamagedException(); } } } /** * Set unique file identifier. Owner identifier is read from description, * identifier from binary content. * * @param ufi Unique file identifier to set. * @exception TagFormatException If File identifier is longer than 64 characters */ public void setUniqueFileIdentifier(TagContent ufi) throws TagFormatException { // check correct format if (ufi.getDescription() == null || ufi.getBinaryContent() == null || checkMaxLength(ufi.getBinaryContent(), 64) == false) { throw new TagFormatException(); } ByteBuilder build = new ByteBuilder(TextFrame.NONE, ufi.getDescription().length() + 2 + ufi.getBinaryContent().length); build.put(ufi.getDescription()); build.put((byte)0); build.put(ufi.getBinaryContent()); (new Frame(id3v2, "UFID", build.getBytes(), true, true, use_compression)).write(); } /** * Read user defined text, store description as description and value as text content * * @return User defined text * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getUserDefinedText() throws FrameDamagedException { byte []v2cont = Frame.read(id3v2, "TXXX"); if (v2cont == null) { return new TagContent(); } else { TagContent ret = new TagContent(); Parser parse = new Parser(v2cont, true, encoding); try { ret.setDescription(parse.parseText()); ret.setContent(parse.parseText()); return ret; } catch (ParseException e) { throw new FrameDamagedException(); } } } /** * Set user defined text information. Description is read from description, * value from text content. * * @param info User defined text information * @exception TagFormatException If information to set is not correctly formatted */ public void setUserDefinedText(TagContent info) throws TagFormatException { if (info.getDescription() == null || info.getTextContent() == null) { throw new TagFormatException(); } ByteBuilder build = new ByteBuilder(TextFrame.UNICODE, info.getDescription().length() * 2 + 3 + info.getTextContent().length() * 2); build.put(info.getDescription()); build.put((byte)0); build.put((byte)0); build.put(info.getTextContent()); (new Frame(id3v2, "TXXX", build.getBytes(), true, true, use_compression)).write(); } /** * Read user defined URL, store description as description and URL as text content * * @return User defined URL link * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getUserDefinedURL() throws FrameDamagedException { byte []v2cont = Frame.read(id3v2, "WXXX"); if (v2cont == null) { return new TagContent(); } else { TagContent ret = new TagContent(); Parser parse = new Parser(v2cont, true, encoding); try { ret.setDescription(parse.parseText()); ret.setContent(parse.parseText(TextFrame.ISO)); return ret; } catch (ParseException e) { throw new FrameDamagedException(); } } } /** * Set user defined URL link. Description is read from description, * URL from text content. * * @param link User defined URL link * @exception TagFormatException If information to set is not correctly formatted */ public void setUserDefinedURL(TagContent link) throws TagFormatException { if (link.getDescription() == null || link.getTextContent() == null) { throw new TagFormatException(); } ByteBuilder build = new ByteBuilder(TextFrame.UNICODE, link.getDescription().length() * 2 + 3 + link.getTextContent().length()); build.put(link.getDescription()); build.put((byte)0); build.put((byte)0); try { build.put(link.getTextContent().getBytes(DEF_ASCII_ENCODING)); } catch (java.io.UnsupportedEncodingException e) {} (new Frame(id3v2, "WXXX", build.getBytes(), true, true, use_compression)).write(); } /** * Read unsynchronized lyrics, store language as type, description as description and * lyrics as text content * * @return Unsynchronized lyrics * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getUnsynchronizedLyrics() throws FrameDamagedException { byte []v2cont = Frame.read(id3v2, "USLT"); if (v2cont == null) { return new TagContent(); } else { TagContent ret = new TagContent(); Parser parse = new Parser(v2cont, true, encoding); try { byte []tmp = parse.parseBinary(3); try { ret.setType(new String(tmp, DEF_ASCII_ENCODING)); } catch (java.io.UnsupportedEncodingException e) {} ret.setDescription(parse.parseText()); ret.setContent(parse.parseText()); return ret; } catch (ParseException e) { throw new FrameDamagedException(); } } } /** * Set unsynchronized lyrics. Language is read from type, Description from description, * lyrics from text content. * * @param lyric Unsynchronized lyrics * @exception TagFormatException If information to set is not correctly formatted */ public void setUnsynchronizedLyrics(TagContent lyric) throws TagFormatException { if (lyric.getType() == null || lyric.getDescription() == null || lyric.getTextContent() == null || !checkExactLength(lyric.getType(), 3)) { throw new TagFormatException(); } ByteBuilder build = new ByteBuilder(TextFrame.UNICODE, 6 + lyric.getDescription().length() * 2 + lyric.getTextContent().length() * 2); try { build.put(lyric.getType().getBytes(DEF_ASCII_ENCODING)); } catch (java.io.UnsupportedEncodingException e) {} build.put(lyric.getDescription()); build.put((byte)0); build.put((byte)0); build.put(lyric.getTextContent()); (new Frame(id3v2, "USLT", build.getBytes(), true, true, use_compression)).write(); } /** * Read comments, store language as type, description as description and * comments as text content * * @return Comments * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getComments() throws FrameDamagedException { TagContent ret = new TagContent(); byte []v2cont = Frame.read(id3v2, "COMM"); if (v2cont == null) { // use v1 try { ret.setContent(id3.getComment()); } catch (Exception e) {} } else { Parser parse = new Parser(v2cont, true, encoding); try { byte []tmp = parse.parseBinary(3); try { ret.setType(new String(tmp, DEF_ASCII_ENCODING)); } catch (java.io.UnsupportedEncodingException e) {} ret.setDescription(parse.parseText()); ret.setContent(parse.parseText()); return ret; } catch (ParseException e) { throw new FrameDamagedException(); } } return ret; } /** * Set comments. Language is read from type, Description from description, * comments from text content. * * @param comm Comments * @exception TagFormatException If information to set is not correctly formatted */ public void setComments(TagContent comm) throws TagFormatException { if (comm.getType() == null || comm.getDescription() == null || comm.getTextContent() == null || !checkExactLength(comm.getType(), 3)) { throw new TagFormatException(); } ByteBuilder build = new ByteBuilder(TextFrame.UNICODE, 4 + comm.getDescription().length() * 2 + comm.getTextContent().length() * 2); try { build.put(comm.getType().getBytes(DEF_ASCII_ENCODING)); // lang code } catch (java.io.UnsupportedEncodingException e) {} build.put(comm.getDescription()); build.put((byte)0); build.put((byte)0); build.put(comm.getTextContent()); (new Frame(id3v2, "COMM", build.getBytes(), true, true, use_compression)).write(); // write id3v1 id3.setComment(comm.getTextContent()); } /** * Read attached picture, store MIME type as type, picture type as binary subtype, * description as description and picture data as binary content. * * @return Picture * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getPicture() throws FrameDamagedException { return id3v2.getPicture(); } /** * Set attached picture. MIME type is read from type, picture type from binary subtype, * description from description, picture data from binary content. * * @param pic Picture * @exception TagFormatException If information to set is not correctly formatted */ public void setPicture(TagContent pic) throws TagFormatException { if (pic.getType() == null || pic.getBinarySubtype() == null || pic.getDescription() == null || pic.getBinaryContent() == null) { throw new TagFormatException("Some null required field(s)"); } ByteBuilder build = new ByteBuilder(TextFrame.UNICODE, 6 + pic.getType().length() + 1 + pic.getDescription().length() * 2 + pic.getBinaryContent().length); try { build.put(pic.getType().getBytes(DEF_ASCII_ENCODING)); } catch (java.io.UnsupportedEncodingException e) {} build.put((byte)0); build.put(pic.getBinarySubtype()[0]); build.put(pic.getDescription()); build.put((byte)0); build.put((byte)0); build.put(pic.getBinaryContent()); (new Frame(id3v2, "APIC", build.getBytes(), true, true, use_compression)).write(); } /** * Read general encapsulated object, store MIME type as type, filename as text subtype, * description as description and object as binary content. * * @return Object * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getEncapsulatedObject() throws FrameDamagedException { byte []v2cont = Frame.read(id3v2, "GEOB"); if (v2cont == null) { return new TagContent(); } else { TagContent ret = new TagContent(); Parser parse = new Parser(v2cont, true, encoding); try { ret.setType(parse.parseText(TextFrame.ISO)); ret.setSubtype(parse.parseText()); ret.setDescription(parse.parseText()); ret.setContent(parse.parseBinary()); return ret; } catch (ParseException e) { throw new FrameDamagedException(); } } } /** * Set general encapsulated object. MIME type is read from type, filename from subtype, * description from description, object from binary content. * * @param obj Object * @exception TagFormatException If information to set is not correctly formatted */ public void setEncapsulatedObject(TagContent obj) throws TagFormatException { if (obj.getType() == null || obj.getTextSubtype() == null || obj.getDescription() == null || obj.getBinaryContent() == null) { throw new TagFormatException(); } ByteBuilder build = new ByteBuilder(TextFrame.UNICODE, 6 + obj.getType().length() + obj.getTextSubtype().length() * 2 + obj.getDescription().length() * 2 + obj.getBinaryContent().length); try { build.put(obj.getType().getBytes(DEF_ASCII_ENCODING)); } catch (java.io.UnsupportedEncodingException e) {} build.put((byte)0); build.put(obj.getTextSubtype()); build.put((byte)0); build.put((byte)0); build.put(obj.getDescription()); build.put((byte)0); build.put((byte)0); build.put(obj.getBinaryContent()); (new Frame(id3v2, "GEOB", build.getBytes(), true, true, use_compression)).write(); } /** * Read terms of use, store language as type and * terms of use as text content * * @return Terms of use * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getTermsOfUse() throws FrameDamagedException { byte []v2cont = Frame.read(id3v2, "USER"); if (v2cont == null) { return new TagContent(); } else { TagContent ret = new TagContent(); Parser parse = new Parser(v2cont, true, encoding); try { byte []tmp = parse.parseBinary(3); try { ret.setType(new String(tmp, DEF_ASCII_ENCODING)); } catch (java.io.UnsupportedEncodingException e) {} ret.setContent(parse.parseText()); return ret; } catch (ParseException e) { throw new FrameDamagedException(); } } } /** * Set terms of use. Language is read from type, * terms of use from text content. * * @param use Terms of use * @exception TagFormatException If information to set is not correctly formatted */ public void setTermsOfUse(TagContent use) throws TagFormatException { if (use.getType() == null || use.getTextContent() == null || !checkExactLength(use.getType(), 3)) { throw new TagFormatException(); } ByteBuilder build = new ByteBuilder(TextFrame.UNICODE, 3 + use.getTextContent().length() * 2); try { build.put(use.getType().getBytes(DEF_ASCII_ENCODING)); } catch (java.io.UnsupportedEncodingException e) {} build.put(use.getTextContent()); (new Frame(id3v2, "USER", build.getBytes(), true, true, use_compression)).write(); } /** * Read encryption method registration, store owner identifier as type, * method symbol as binary subtype and encryption data as binary content. * * @return Encryption method registration * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getEncryptionMethodRegistration() throws FrameDamagedException { byte []v2cont = Frame.read(id3v2, "ENCR"); if (v2cont == null) { return new TagContent(); } else { TagContent ret = new TagContent(); Parser parse = new Parser(v2cont, false); try { ret.setType(parse.parseText()); ret.setSubtype(parse.parseBinary(1)); ret.setContent(parse.parseBinary()); return ret; } catch (ParseException e) { throw new FrameDamagedException(); } } } /** * Set encryption method registration. Owner identifier is read from type, * method symbol from binary subtype and encryption data from binary content. * * @param encr Encryption method * @exception TagFormatException If information to set is not correctly formatted */ public void setEncryptionMethodRegistration(TagContent encr) throws TagFormatException { if (encr.getType() == null || encr.getBinarySubtype() == null || encr.getBinaryContent() == null || !checkExactLength(encr.getBinarySubtype(), 1)) { throw new TagFormatException(); } ByteBuilder build = new ByteBuilder(TextFrame.NONE, 2 + encr.getType().length() + encr.getBinaryContent().length); build.put(encr.getType()); build.put((byte)0); build.put(encr.getBinarySubtype()[0]); build.put(encr.getBinaryContent()); (new Frame(id3v2, "ENCR", build.getBytes(), true, true, use_compression)).write(); } /** * Read group identification registration, store owner identifier as type, * group symbol as binary subtype and group dependent data as binary content. * * @return Group identification registration * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getGroupIdentificationRegistration() throws FrameDamagedException { byte []v2cont = Frame.read(id3v2, "GRID"); if (v2cont == null) { return new TagContent(); } else { TagContent ret = new TagContent(); Parser parse = new Parser(v2cont, false); try { ret.setType(parse.parseText()); ret.setSubtype(parse.parseBinary(1)); ret.setContent(parse.parseBinary()); return ret; } catch (ParseException e) { throw new FrameDamagedException(); } } } /** * Set group identification registration. Owner identifier is read from type, * group symbol from binary subtype and group dependent data from binary content. * * @param grp Group identification * @exception TagFormatException If information to set is not correctly formatted */ public void setGroupIdentificationRegistration(TagContent grp) throws TagFormatException { if (grp.getType() == null || grp.getBinarySubtype() == null || grp.getBinaryContent() == null || !checkExactLength(grp.getBinarySubtype(), 1)) { throw new TagFormatException(); } ByteBuilder build = new ByteBuilder(TextFrame.NONE, 2 + grp.getType().length() + grp.getBinaryContent().length); build.put(grp.getType()); build.put((byte)0); build.put(grp.getBinarySubtype()[0]); build.put(grp.getBinaryContent()); (new Frame(id3v2, "GRID", build.getBytes(), true, true, use_compression)).write(); } /** * Read private data, store owner identifier as type, * private data as binary content. * * @return Private data * @exception FrameDamagedException If frame is damaged (e.g. too short) */ public TagContent getPrivateData() throws FrameDamagedException { byte []v2cont = Frame.read(id3v2, "PRIV"); if (v2cont == null) { return new TagContent(); } else { TagContent ret = new TagContent(); Parser parse = new Parser(v2cont, false); try { ret.setType(parse.parseText()); ret.setContent(parse.parseBinary()); return ret; } catch (ParseException e) { throw new FrameDamagedException(); } } } /** * Set private data. Owner identifier is read from type, * private data from binary content. * * @param data Private data * @exception TagFormatException If information to set is not correctly formatted */ public void setPrivateData(TagContent data) throws TagFormatException { if (data.getType() == null || data.getBinaryContent() == null) { throw new TagFormatException(); } ByteBuilder build = new ByteBuilder(TextFrame.NONE, 1 + data.getType().length() + data.getBinaryContent().length); build.put(data.getType()); build.put((byte)0); build.put(data.getBinaryContent()); (new Frame(id3v2, "PRIV", build.getBytes(), true, true, use_compression)).write(); } /********** Private fields **********/ /** * Write ID3 tag? */ protected boolean write_id3 = true; /** * Write ID3v2 tag? */ protected boolean write_id3v2 = true; /** * Use compression in ID3v2 tag? */ protected boolean use_compression = true; /** * Use CRC in ID3v2 tag? */ protected boolean use_crc = true; /** * Use padding in ID3v2 tag? */ protected boolean use_padding = true; /** * Use unsynchronization in ID3v2 tag? */ protected boolean use_unsynchronization = true; /** * ID3 tag */ protected ID3 id3 = null; /** * ID3v3 tag */ protected ID3v2 id3v2 = null; /** * MP3 properties */ protected MP3Properties prop = null; /** * encoding used for conversion Java string to/from byte sequence */ protected String encoding; /** common stuff for each constructor */ protected void reset(String encoding) throws IOException, NoMP3FrameException, ID3v2WrongCRCException, ID3v2DecompressionException, ID3v2IllegalVersionException { prop = new MP3Properties(this); id3 = new ID3(this, encoding); id3v2 = new ID3v2(this, encoding); setTagsEncoding(encoding); } /********** Private methods **********/ /** * Checks if input string is of a given length */ protected boolean checkExactLength(String in, int length) { return (in.length() == length); } /** * Checks if input string has a maximum given length */ protected boolean checkMaxLength(String in, int length) { return (in.length() <= length); } /** * Checks if input byte array is of a given length */ protected boolean checkExactLength(byte []in, int length) { return (in.length == length); } /** * Checks if input byte array has a maximum given length */ protected boolean checkMaxLength(byte []in, int length) { return (in.length <= length); } /** * Checks if input string is numeric */ protected boolean checkNumeric(String in) { try { Integer test = new Integer(in); return true; } catch (NumberFormatException e) { return false; } } } // public class MP3File