EMMA Coverage Report (generated Tue Mar 14 21:50:42 EST 2006)
[all classes][org.farng.mp3]

COVERAGE SUMMARY FOR SOURCE FILE [MP3File.java]

nameclass, %method, %block, %line, %
MP3File.java100% (1/1)49%  (28/57)74%  (1211/1636)74%  (296.1/402)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class MP3File100% (1/1)49%  (28/57)74%  (1211/1636)74%  (296.1/402)
MP3File (MP3File): void 0%   (0/1)0%   (0/88)0%   (0/20)
MP3File (String): void 0%   (0/1)0%   (0/7)0%   (0/2)
delete (AbstractMP3Tag): void 0%   (0/1)0%   (0/9)0%   (0/2)
getBitRate (): int 0%   (0/1)0%   (0/3)0%   (0/1)
getEmphasis (): byte 0%   (0/1)0%   (0/3)0%   (0/1)
getFrequency (): double 0%   (0/1)0%   (0/3)0%   (0/1)
getLayer (): byte 0%   (0/1)0%   (0/3)0%   (0/1)
getMode (): byte 0%   (0/1)0%   (0/3)0%   (0/1)
getModeExtension (): byte 0%   (0/1)0%   (0/3)0%   (0/1)
getMpegVersion (): byte 0%   (0/1)0%   (0/3)0%   (0/1)
hasFilenameTag (): boolean 0%   (0/1)0%   (0/7)0%   (0/1)
hasID3v1Tag (): boolean 0%   (0/1)0%   (0/7)0%   (0/1)
hasLyrics3Tag (): boolean 0%   (0/1)0%   (0/7)0%   (0/1)
isCopyProtected (): boolean 0%   (0/1)0%   (0/3)0%   (0/1)
isHome (): boolean 0%   (0/1)0%   (0/3)0%   (0/1)
isPadding (): boolean 0%   (0/1)0%   (0/3)0%   (0/1)
isPrivacy (): boolean 0%   (0/1)0%   (0/3)0%   (0/1)
isProtection (): boolean 0%   (0/1)0%   (0/3)0%   (0/1)
isUnsynchronized (): boolean 0%   (0/1)0%   (0/8)0%   (0/1)
isVariableBitRate (): boolean 0%   (0/1)0%   (0/3)0%   (0/1)
save (File): void 0%   (0/1)0%   (0/6)0%   (0/2)
save (String): void 0%   (0/1)0%   (0/9)0%   (0/2)
save (String, int): void 0%   (0/1)0%   (0/8)0%   (0/2)
save (int): void 0%   (0/1)0%   (0/6)0%   (0/2)
seekMP3Frame (): boolean 0%   (0/1)0%   (0/29)0%   (0/7)
setID3v1Tag (AbstractMP3Tag): void 0%   (0/1)0%   (0/7)0%   (0/2)
setID3v2Tag (AbstractMP3Tag): void 0%   (0/1)0%   (0/7)0%   (0/2)
setLyrics3Tag (AbstractMP3Tag): void 0%   (0/1)0%   (0/7)0%   (0/2)
setVariableBitRate (boolean): void 0%   (0/1)0%   (0/4)0%   (0/2)
getFrameSize (): int 100% (1/1)63%  (31/49)71%  (5/7)
getMp3StartByte (File): long 100% (1/1)77%  (24/31)91%  (7.3/8)
save (File, int): void 100% (1/1)79%  (148/188)83%  (39/47)
MP3File (File, boolean): void 100% (1/1)80%  (78/98)88%  (30/34)
readFrameHeader (RandomAccessFile): void 100% (1/1)82%  (199/244)66%  (31.9/48)
seekMP3Frame (RandomAccessFile): boolean 100% (1/1)88%  (52/59)78%  (15.6/20)
adjustID3v2Padding (int, boolean, boolean, File): boolean 100% (1/1)91%  (256/280)94%  (57.4/61)
seekNextMP3Frame (RandomAccessFile, int): boolean 100% (1/1)93%  (65/70)90%  (19/21)
getUnsynchronizedFragments (): Set 100% (1/1)97%  (118/122)96%  (27/28)
MP3File (): void 100% (1/1)100% (3/3)100% (2/2)
MP3File (File): void 100% (1/1)100% (5/5)100% (2/2)
adjustID3v2Padding (): boolean 100% (1/1)100% (11/11)100% (1/1)
adjustID3v2Padding (int, boolean, boolean): boolean 100% (1/1)100% (8/8)100% (1/1)
getFilenameTag (): FilenameTag 100% (1/1)100% (3/3)100% (1/1)
getFrameAcrossTags (String): List 100% (1/1)100% (110/110)100% (26/26)
getID3v1Tag (): ID3v1 100% (1/1)100% (3/3)100% (1/1)
getID3v2Tag (): AbstractID3v2 100% (1/1)100% (3/3)100% (1/1)
getLyrics3Tag (): AbstractLyrics3 100% (1/1)100% (3/3)100% (1/1)
getMp3StartByte (): long 100% (1/1)100% (5/5)100% (1/1)
getMp3file (): File 100% (1/1)100% (3/3)100% (1/1)
hasID3v2Tag (): boolean 100% (1/1)100% (7/7)100% (1/1)
save (): void 100% (1/1)100% (7/7)100% (2/2)
setFilenameTag (FilenameTag): void 100% (1/1)100% (4/4)100% (2/2)
setFrameAcrossTags (AbstractID3v2Frame): void 100% (1/1)100% (49/49)100% (13/13)
setID3v1Tag (ID3v1): void 100% (1/1)100% (4/4)100% (2/2)
setID3v2Tag (AbstractID3v2): void 100% (1/1)100% (4/4)100% (2/2)
setLyrics3Tag (AbstractLyrics3): void 100% (1/1)100% (4/4)100% (2/2)
setMp3file (File): void 100% (1/1)100% (4/4)100% (2/2)

1package org.farng.mp3;
2 
3import org.farng.mp3.filename.FilenameTag;
4import org.farng.mp3.filename.FilenameTagBuilder;
5import org.farng.mp3.id3.AbstractID3v2;
6import org.farng.mp3.id3.AbstractID3v2Frame;
7import org.farng.mp3.id3.ID3v1;
8import org.farng.mp3.id3.ID3v1_1;
9import org.farng.mp3.id3.ID3v2_2;
10import org.farng.mp3.id3.ID3v2_3;
11import org.farng.mp3.id3.ID3v2_4;
12import org.farng.mp3.lyrics3.AbstractLyrics3;
13import org.farng.mp3.lyrics3.Lyrics3v1;
14import org.farng.mp3.lyrics3.Lyrics3v2;
15 
16import java.io.EOFException;
17import java.io.File;
18import java.io.FileInputStream;
19import java.io.FileNotFoundException;
20import java.io.FileOutputStream;
21import java.io.IOException;
22import java.io.RandomAccessFile;
23import java.util.ArrayList;
24import java.util.HashSet;
25import java.util.Iterator;
26import java.util.List;
27import java.util.Set;
28 
29/**
30 * <TABLE border=0> <TBODY> <TR> <TD class=h2>How is MP3 built?</TD></TR></TBODY></TABLE> <TABLE border=0> <TBODY> <TR
31 * vAlign=top> <TD> <P>Most people with a little knowledge in MP3 files know that the sound is divided into smaller
32 * parts and compressed with a psycoacoustic model. This smaller pieces of the audio is then put into something called
33 * 'frames', which is a little datablock with a header. I'll focus on that header in this text.</P>
34 * <p/>
35 * <P>The header is 4 bytes, 32 bits, big and begins with something called sync. This sync is, at least according to the
36 * MPEG standard, 12 set bits in a row. Some add-on standards made later uses 11 set bits and one cleared bit. The sync
37 * is directly followed by a ID bit, indicating if the file is a MPEG-1 och MPEG-2 file. 0=MPEG-2 and 1=MPEG-1</P>
38 * <p/>
39 * <P>The layer is defined with the two layers bits. They are oddly defined as</P> <CENTER> <TABLE cellSpacing=0
40 * cellPadding=2 border=1> <TBODY> <TR> <TD>0 0</TD> <TD>Not defined</TD></TR> <TR> <TD>0 1</TD> <TD>Layer III</TD></TR>
41 * <TR> <TD>1 0</TD> <TD>Layer II</TD></TR> <TR> <TD>1 1</TD> <TD>Layer I</TD></TR></TBODY></TABLE> </CENTER>
42 * <p/>
43 * <P>With this information and the information in the bitrate field we can determine the bitrate of the audio (in
44 * kbit/s) according to this table.</P> <CENTER> <TABLE cellSpacing=0 cellPadding=2 border=1> <TBODY> <TR>
45 * <TD>Bitrate<BR>value</TD> <TD>MPEG-1,<BR>layer I</TD> <TD>MPEG-1,<BR>layer II</TD> <TD>MPEG-1,<BR>layer III</TD>
46 * <TD>MPEG-2,<BR>layer I</TD> <TD>MPEG-2,<BR>layer II</TD> <TD>MPEG-2,<BR>layer III</TD></TR> <TR> <TD>0 0 0 0</TD>
47 * <TD></TD> <TD></TD> <TD></TD> <TD></TD> <TD></TD> <TD></TD></TR> <TR> <TD>0 0 0 1</TD> <TD>32</TD> <TD>32</TD>
48 * <TD>32</TD> <TD>32</TD> <TD>32</TD> <TD>8</TD></TR> <TR> <TD>0 0 1 0</TD> <TD>64</TD> <TD>48</TD> <TD>40</TD>
49 * <TD>64</TD> <TD>48</TD> <TD>16</TD></TR> <TR> <TD>0 0 1 1</TD> <TD>96</TD> <TD>56</TD> <TD>48</TD> <TD>96</TD>
50 * <TD>56</TD> <TD>24</TD></TR> <TR> <TD>0 1 0 0</TD> <TD>128</TD> <TD>64</TD> <TD>56</TD> <TD>128</TD> <TD>64</TD>
51 * <TD>32</TD></TR> <TR> <TD>0 1 0 1</TD> <TD>160</TD> <TD>80</TD> <TD>64</TD> <TD>160</TD> <TD>80</TD> <TD>64</TD></TR>
52 * <TR> <TD>0 1 1 0</TD> <TD>192</TD> <TD>96</TD> <TD>80</TD> <TD>192</TD> <TD>96</TD> <TD>80</TD></TR> <TR> <TD>0 1 1
53 * 1</TD> <TD>224</TD> <TD>112</TD> <TD>96</TD> <TD>224</TD> <TD>112</TD> <TD>56</TD></TR> <TR> <TD>1 0 0 0</TD>
54 * <TD>256</TD> <TD>128</TD> <TD>112</TD> <TD>256</TD> <TD>128</TD> <TD>64</TD></TR> <TR> <TD>1 0 0 1</TD> <TD>288</TD>
55 * <TD>160</TD> <TD>128</TD> <TD>288</TD> <TD>160</TD> <TD>128</TD></TR> <TR> <TD>1 0 1 0</TD> <TD>320</TD> <TD>192</TD>
56 * <TD>160</TD> <TD>320</TD> <TD>192</TD> <TD>160</TD></TR> <TR> <TD>1 0 1 1</TD> <TD>352</TD> <TD>224</TD> <TD>192</TD>
57 * <TD>352</TD> <TD>224</TD> <TD>112</TD></TR> <TR> <TD>1 1 0 0</TD> <TD>384</TD> <TD>256</TD> <TD>224</TD> <TD>384</TD>
58 * <TD>256</TD> <TD>128</TD></TR> <TR> <TD>1 1 0 1</TD> <TD>416</TD> <TD>320</TD> <TD>256</TD> <TD>416</TD> <TD>320</TD>
59 * <TD>256</TD></TR> <TR> <TD>1 1 1 0</TD> <TD>448</TD> <TD>384</TD> <TD>320</TD> <TD>448</TD> <TD>384</TD>
60 * <TD>320</TD></TR> <TR> <TD>1 1 1 1</TD> <TD></TD> <TD></TD> <TD></TD> <TD></TD> <TD></TD>
61 * <TD></TD></TR></TBODY></TABLE> </CENTER>
62 * <p/>
63 * <P>The sample rate is described in the frequency field. These values is dependent of which MPEG standard is used
64 * according to the following table.</P> <CENTER> <TABLE cellSpacing=0 cellPadding=2 border=1> <TBODY> <TR>
65 * <TD>Frequency<BR>value</TD> <TD>MPEG-1</TD> <TD>MPEG-2</TD></TR> <TR> <TD>0 0</TD> <TD>44100 Hz</TD> <TD>22050
66 * Hz</TD></TR> <TR> <TD>0 1</TD> <TD>48000 Hz</TD> <TD>24000 Hz</TD></TR> <TR> <TD>1 0</TD> <TD>32000 Hz</TD> <TD>16000
67 * Hz</TD></TR> <TR> <TD>1 1</TD> <TD></TD> <TD></TD></TR></TBODY></TABLE> </CENTER>
68 * <p/>
69 * <P>Three bits is not needed in the decoding process at all. These are the copyright bit, original home bit and the
70 * private bit. The copyright has the same meaning as the copyright bit on CDs and DAT tapes, i.e. telling that it is
71 * illegal to copy the contents if the bit is set. The original home bit indicates, if set, that the frame is located on
72 * its original media. No one seems to know what the privat bit is good for.
73 * <p/>
74 * <p/>
75 * <p/>
76 * <P>If the protection bit is NOT set then the frame header is followed by a 16 bit checksum, inserted before the audio
77 * data. If the padding bit is set then the frame is padded with an extra byte. Knowing this the size of the complete
78 * frame can be calculated with the following formula</P> <CENTER> <P>FrameSize = 144 * BitRate / SampleRate<BR>when the
79 * padding bit is cleared and</P>
80 * <p/>
81 * <P>FrameSize = (144 * BitRate / SampleRate) + 1<BR>when the padding bit is set.
82 * <p/>
83 * <P></CENTER>
84 * <p/>
85 * <P>The frameSize is of course an integer. If for an example BitRate=128000, SampleRate=44100 and the padding bit is
86 * cleared, then the FrameSize = 144 * 128000 / 44100 = 417
87 * <p/>
88 * <p/>
89 * <p/>
90 * <P>The mode field is used to tell which sort of stereo/mono encoding that has been used. The purpose of the mode
91 * extension field is different for different layers, but I really don't know exactly what it's for.</P> <CENTER> <TABLE
92 * cellSpacing=0 cellPadding=2 border=1> <TBODY> <TR> <TD>Mode value</TD> <TD>mode</TD></TR> <TR> <TD>0 0</TD>
93 * <TD>Stereo</TD></TR> <TR> <TD>0 1</TD> <TD>Joint stereo</TD></TR> <TR> <TD>1 0</TD> <TD>Dual channel</TD></TR> <TR>
94 * <TD>1 1</TD> <TD>Mono</TD></TR></TBODY></TABLE> </CENTER>
95 * <p/>
96 * <P>The last field is the emphasis field. It is used to sort of 're-equalize' the sound after a Dolby-like noise
97 * supression. This is not very used and will probably never be. The following noise supression model is used</P>
98 * <CENTER> <TABLE cellSpacing=0 cellPadding=2 border=1> <TBODY> <TR> <TD>Emphasis value</TD> <TD>Emphasis
99 * method</TD></TR> <TR> <TD>0 0</TD> <TD>none</TD></TR> <TR> <TD>0 1</TD> <TD>50/15ms</TD></TR> <TR> <TD>1 0</TD>
100 * <TD></TD></TR> <TR> <TD>1 1</TD> <TD>CCITT j.17</TD></TR></TBODY></TABLE> </CENTER></TD> </TR></TBODY></TABLE>
101 *
102 * @author Eric Farng
103 * @version $Revision: 1.5 $
104 */
105public class MP3File {
106 
107    /**
108     * the ID3v2 tag that this file contains.
109     */
110    private AbstractID3v2 id3v2tag;
111    /**
112     * the Lyrics3 tag that this file contains.
113     */
114    private AbstractLyrics3 lyrics3tag;
115    /**
116     * the mp3 file that this instance represents. This value can be null. This value is also used for any methods that
117     * are called without a file argument
118     */
119    private File mp3file;
120    /**
121     * the ID3v2_4 tag that represents the parsed filename.
122     */
123    private FilenameTag filenameTag;
124    /**
125     * the ID3v1 tag that this file contains.
126     */
127    private ID3v1 id3v1tag;
128    /**
129     * value read from the MP3 Frame header
130     */
131    private boolean copyProtected;
132    /**
133     * value read from the MP3 Frame header
134     */
135    private boolean home;
136    /**
137     * value read from the MP3 Frame header
138     */
139    private boolean padding;
140    /**
141     * value read from the MP3 Frame header
142     */
143    private boolean privacy;
144    /**
145     * value read from the MP3 Frame header
146     */
147    private boolean protection;
148    /**
149     * value read from the MP3 Frame header
150     */
151    private boolean variableBitRate;
152    /**
153     * value read from the MP3 Frame header
154     */
155    private byte emphasis;
156    /**
157     * value read from the MP3 Frame header
158     */
159    private byte layer;
160    /**
161     * value read from the MP3 Frame header
162     */
163    private byte mode;
164    /**
165     * value read from the MP3 Frame header
166     */
167    private byte modeExtension;
168    /**
169     * value read from the MP3 Frame header
170     */
171    private byte mpegVersion;
172    /**
173     * frequency determined from MP3 Version and frequency value read from the MP3 Frame header
174     */
175    private double frequency;
176    /**
177     * bitrate calculated from the frame MP3 Frame header
178     */
179    private int bitRate;
180 
181    /**
182     * Creates a new empty MP3File object that is not associated with a specific file.
183     */
184    public MP3File() {
185        super();
186    }
187 
188    /**
189     * Creates a new MP3File object.
190     */
191    public MP3File(final MP3File copyObject) {
192        super();
193        copyProtected = copyObject.copyProtected;
194        home = copyObject.home;
195        padding = copyObject.padding;
196        privacy = copyObject.privacy;
197        protection = copyObject.protection;
198        variableBitRate = copyObject.variableBitRate;
199        emphasis = copyObject.emphasis;
200        layer = copyObject.layer;
201        mode = copyObject.mode;
202        modeExtension = copyObject.modeExtension;
203        mpegVersion = copyObject.mpegVersion;
204        frequency = copyObject.frequency;
205        bitRate = copyObject.bitRate;
206        mp3file = new File(copyObject.mp3file.getAbsolutePath());
207        filenameTag = new FilenameTag(copyObject.filenameTag);
208        id3v2tag = (AbstractID3v2) TagUtility.copyObject(copyObject.id3v2tag);
209        lyrics3tag = (AbstractLyrics3) TagUtility.copyObject(copyObject.lyrics3tag);
210        id3v1tag = (ID3v1) TagUtility.copyObject(copyObject.id3v1tag);
211    }
212 
213    /**
214     * Creates a new MP3File object and parse the tag from the given filename.
215     *
216     * @param filename MP3 file
217     *
218     * @throws IOException  on any I/O error
219     * @throws TagException on any exception generated by this library.
220     */
221    public MP3File(final String filename) throws IOException, TagException {
222        this(new File(filename));
223    }
224 
225    /**
226     * Creates a new MP3File object and parse the tag from the given file Object.
227     *
228     * @param file MP3 file
229     *
230     * @throws IOException  on any I/O error
231     * @throws TagException on any exception generated by this library.
232     */
233    public MP3File(final File file) throws IOException, TagException {
234        this(file, true);
235    }
236 
237    /**
238     * Creates a new MP3File object and parse the tag from the given file Object.
239     *
240     * @param file      MP3 file
241     * @param writeable open in read (false) or read-write (true) mode
242     *
243     * @throws IOException  on any I/O error
244     * @throws TagException on any exception generated by this library.
245     */
246    public MP3File(final File file, final boolean writeable) throws IOException, TagException {
247        super();
248        mp3file = file;
249        final RandomAccessFile newFile = new RandomAccessFile(file, writeable ? "rw" : "r");
250        try {
251            id3v1tag = new ID3v1_1(newFile);
252        } catch (TagNotFoundException ex) {
253            // tag might be different version
254        }
255        try {
256            if (id3v1tag == null) {
257                id3v1tag = new ID3v1(newFile);
258            }
259        } catch (TagNotFoundException ex) {
260            // ok if it's null
261        }
262        try {
263            id3v2tag = new ID3v2_4(newFile);
264        } catch (TagNotFoundException ex) {
265            // maybe different version
266        }
267        try {
268            if (id3v2tag == null) {
269                id3v2tag = new ID3v2_3(newFile);
270            }
271        } catch (TagNotFoundException ex) {
272            // maybe a different version
273        }
274        try {
275            if (id3v2tag == null) {
276                id3v2tag = new ID3v2_2(newFile);
277            }
278        } catch (TagNotFoundException ex) {
279            // it's ok to be null
280        }
281        try {
282            lyrics3tag = new Lyrics3v2(newFile);
283        } catch (TagNotFoundException ex) {
284            // maybe a different version
285        }
286        try {
287            if (lyrics3tag == null) {
288                lyrics3tag = new Lyrics3v1(newFile);
289            }
290        } catch (TagNotFoundException ex) {
291            //it's ok to be null
292        }
293        newFile.close();
294        try {
295            filenameTag = FilenameTagBuilder.createFilenameTagFromMP3File(this);
296        } catch (Exception ex) {
297            throw new TagException("Unable to create FilenameTag", ex);
298        }
299    }
300 
301    public int getBitRate() {
302        return bitRate;
303    }
304 
305    public boolean isCopyProtected() {
306        return copyProtected;
307    }
308 
309    public byte getEmphasis() {
310        return emphasis;
311    }
312 
313    /**
314     * Sets the filename tag for this MP3 File. Refer to <code>TagUtilities.parseFileName</code> and
315     * <code>TagUtilities.createID3v2Tag</code> for more information about parsing file names into <code>ID3v2_4</code>
316     * objects.
317     *
318     * @param filenameTag parsed <code>ID3v2_4</code> filename tag
319     */
320    public void setFilenameTag(final FilenameTag filenameTag) {
321        this.filenameTag = filenameTag;
322    }
323 
324    /**
325     * Sets the filename tag for this MP3 File. Refer to <code>TagUtilities.parseFileName</code> and
326     * <code>TagUtilities.createID3v2Tag</code> for more information about parsing file names into <code>ID3v2_4</code>
327     * objects.
328     *
329     * @return parsed <code>ID3v2_4</code> filename tag
330     */
331    public FilenameTag getFilenameTag() {
332        return filenameTag;
333    }
334 
335    /**
336     * Sets all four (id3v1, lyrics3, filename, id3v2) tags in this instance to the <code>frame</code> argument if the
337     * tag exists. This method does not use the options inside the <code>tagOptions</code> object.
338     *
339     * @param frame frame to set / replace in all four tags.
340     */
341    //todo this method is very inefficient.
342    public void setFrameAcrossTags(final AbstractID3v2Frame frame) {
343        if (id3v1tag != null) {
344            final ID3v2_4 id3v1 = new ID3v2_4(id3v1tag);
345            id3v1.setFrame(frame);
346            id3v1tag.overwrite(id3v1);
347        }
348        if (id3v2tag != null) {
349            id3v2tag.setFrame(frame);
350        }
351        if (lyrics3tag != null) {
352            final ID3v2_4 lyrics3 = new ID3v2_4(lyrics3tag);
353            lyrics3.setFrame(frame);
354            lyrics3tag = new Lyrics3v2(lyrics3);
355        }
356        if (filenameTag != null) {
357            filenameTag.setFrame(frame);
358        }
359    }
360 
361    /**
362     * Gets the frames from all four (id3v1, lyrics3, filename, id3v2) mp3 tags in this instance for each tag that
363     * exists. This method does not use the options inside the <code>tagOptions</code> object.
364     *
365     * @param identifier ID3v2.4 Tag Frame Identifier.
366     *
367     * @return ArrayList of all instances of the desired frame. Each instance is returned as an
368     *         <code>ID3v2_4Frame</code>. The nature of the code returns the array in a specific order, but this order
369     *         is not guaranteed for future versions of this library.
370     */
371    //todo this method is very inefficient.
372    public List getFrameAcrossTags(final String identifier) {
373        if (identifier != null && identifier.length() > 0) {
374            final List list = new ArrayList(32);
375            Iterator iterator;
376            if (id3v1tag != null) {
377                final ID3v2_4 id3v1 = new ID3v2_4(id3v1tag);
378                if (id3v1.hasFrameOfType(identifier)) {
379                    iterator = id3v1.getFrameOfType(identifier);
380                    while (iterator.hasNext()) {
381                        list.add(iterator.next());
382                    }
383                }
384            }
385            if (id3v2tag != null) {
386                if (id3v2tag.hasFrameOfType(identifier)) {
387                    iterator = id3v2tag.getFrameOfType(identifier);
388                    while (iterator.hasNext()) {
389                        list.add(iterator.next());
390                    }
391                }
392            }
393            if (lyrics3tag != null) {
394                final ID3v2_4 lyrics3 = new ID3v2_4(lyrics3tag);
395                if (lyrics3.hasFrameOfType(identifier)) {
396                    iterator = lyrics3.getFrameOfType(identifier);
397                    while (iterator.hasNext()) {
398                        list.add(iterator.next());
399                    }
400                }
401            }
402            if (filenameTag != null) {
403                if (filenameTag.hasFrameOfType(identifier)) {
404                    iterator = filenameTag.getFrameOfType(identifier);
405                    while (iterator.hasNext()) {
406                        list.add(iterator.next());
407                    }
408                }
409            }
410            return list;
411        }
412        return null;
413    }
414 
415    public double getFrequency() {
416        return frequency;
417    }
418 
419    public boolean isHome() {
420        return home;
421    }
422 
423    /**
424     * Sets the <code>ID3v1</code> tag for this object. A new <code>ID3v1_1</code> object is created from the argument
425     * and then used here.
426     *
427     * @param mp3tag Any MP3Tag object can be used and will be converted into a new ID3v1_1 object.
428     */
429    public void setID3v1Tag(final AbstractMP3Tag mp3tag) {
430        id3v1tag = new ID3v1_1(mp3tag);
431    }
432 
433    public void setID3v1Tag(final ID3v1 id3v1tag) {
434        this.id3v1tag = id3v1tag;
435    }
436 
437    /**
438     * Returns the <code>ID3v1</code> tag for this object.
439     *
440     * @return the <code>ID3v1</code> tag for this object
441     */
442    public ID3v1 getID3v1Tag() {
443        return id3v1tag;
444    }
445 
446    /**
447     * Sets the <code>ID3v2</code> tag for this object. A new <code>ID3v2_4</code> object is created from the argument
448     * and then used here.
449     *
450     * @param mp3tag Any MP3Tag object can be used and will be converted into a new ID3v2_4 object.
451     */
452    public void setID3v2Tag(final AbstractMP3Tag mp3tag) {
453        id3v2tag = new ID3v2_4(mp3tag);
454    }
455 
456    public void setID3v2Tag(final AbstractID3v2 id3v2tag) {
457        this.id3v2tag = id3v2tag;
458    }
459 
460    /**
461     * Returns the <code>ID3v2</code> tag for this object.
462     *
463     * @return the <code>ID3v2</code> tag for this object
464     */
465    public AbstractID3v2 getID3v2Tag() {
466        return id3v2tag;
467    }
468 
469    public byte getLayer() {
470        return layer;
471    }
472 
473    /**
474     * Sets the <code>Lyrics3</code> tag for this object. A new <code>Lyrics3v2</code> object is created from the
475     * argument and then used here.
476     *
477     * @param mp3tag Any MP3Tag object can be used and will be converted into a new Lyrics3v2 object.
478     */
479    public void setLyrics3Tag(final AbstractMP3Tag mp3tag) {
480        lyrics3tag = new Lyrics3v2(mp3tag);
481    }
482 
483    public void setLyrics3Tag(final AbstractLyrics3 lyrics3tag) {
484        this.lyrics3tag = lyrics3tag;
485    }
486 
487    /**
488     * Returns the <code>ID3v1</code> tag for this object.
489     *
490     * @return the <code>ID3v1</code> tag for this object
491     */
492    public AbstractLyrics3 getLyrics3Tag() {
493        return lyrics3tag;
494    }
495 
496    public byte getMode() {
497        return mode;
498    }
499 
500    public byte getModeExtension() {
501        return modeExtension;
502    }
503 
504    /**
505     * Returns the byte position of the first MP3 Frame that this object refers to. This is the first byte of music data
506     * and not the ID3 Tag Frame.
507     *
508     * @return the byte position of the first MP3 Frame
509     *
510     * @throws IOException           on any I/O error
511     * @throws FileNotFoundException if the file exists but is a directory rather than a regular file or cannot be
512     *                               opened for any other reason
513     */
514    public long getMp3StartByte() throws IOException, FileNotFoundException {
515        return getMp3StartByte(mp3file);
516    }
517 
518    /**
519     * Returns the byte position of the first MP3 Frame that the <code>file</code> arguement refers to. This is the
520     * first byte of music data and not the ID3 Tag Frame.
521     *
522     * @param file MP3 file to search
523     *
524     * @return the byte position of the first MP3 Frame
525     *
526     * @throws IOException           on any I/O error
527     * @throws FileNotFoundException if the file exists but is a directory rather than a regular file or cannot be
528     *                               opened for any other reason
529     */
530    public long getMp3StartByte(final File file) throws IOException, FileNotFoundException {
531        RandomAccessFile rfile = null;
532        long startByte = 0L;
533        try {
534            rfile = new RandomAccessFile(file, "r");
535            seekMP3Frame(rfile);
536            startByte = rfile.getFilePointer();
537        } finally {
538            if (rfile != null) {
539                rfile.close();
540            }
541        }
542        return startByte;
543    }
544 
545    public void setMp3file(final File mp3file) {
546        this.mp3file = mp3file;
547    }
548 
549    public File getMp3file() {
550        return mp3file;
551    }
552 
553    public byte getMpegVersion() {
554        return mpegVersion;
555    }
556 
557    public boolean isPadding() {
558        return padding;
559    }
560 
561    public boolean isPrivacy() {
562        return privacy;
563    }
564 
565    public boolean isProtection() {
566        return protection;
567    }
568 
569    /**
570     * Returns true if there are any unsynchronized tags in this object. A fragment is unsynchronized if it exists in
571     * two or more tags but is not equal across all of them.
572     *
573     * @return true of any fragments are unsynchronized.
574     */
575    //todo there might be a faster way to do this, other than calling
576    //getUnsynchronizedFragments()
577    public boolean isUnsynchronized() {
578        return getUnsynchronizedFragments().size() > 0;
579    }
580 
581    /**
582     * Returns a HashSet of unsynchronized fragments across all tags in this object. A fragment is unsynchronized if it
583     * exists in two or more tags but is not equal across all of them.
584     *
585     * @return a HashSet of unsynchronized fragments
586     */
587    public Set getUnsynchronizedFragments() {
588        final ID3v2_4 total = new ID3v2_4(id3v2tag);
589        final Set set = new HashSet(32);
590        total.append(id3v1tag);
591        total.append(lyrics3tag);
592        total.append(filenameTag);
593        total.append(id3v2tag);
594        final ID3v2_4 id3v1 = new ID3v2_4(id3v1tag);
595        final ID3v2_4 lyrics3 = new ID3v2_4(lyrics3tag);
596        final ID3v2_4 filename = new ID3v2_4(filenameTag);
597        final AbstractID3v2 id3v2 = id3v2tag;
598        final Iterator iterator = total.iterator();
599        while (iterator.hasNext()) {
600            final AbstractID3v2Frame frame = (AbstractID3v2Frame) iterator.next();
601            final String identifier = frame.getIdentifier();
602            if (id3v2 != null) {
603                if (id3v2.hasFrame(identifier)) {
604                    if (!id3v2.getFrame(identifier).isSubsetOf(frame)) {
605                        set.add(identifier);
606                    }
607                }
608            }
609            if (id3v1.hasFrame(identifier)) {
610                if (!id3v1.getFrame(identifier).isSubsetOf(frame)) {
611                    set.add(identifier);
612                }
613            }
614            if (lyrics3.hasFrame(identifier)) {
615                if (!lyrics3.getFrame(identifier).isSubsetOf(frame)) {
616                    set.add(identifier);
617                }
618            }
619            if (filename.hasFrame(identifier)) {
620                if (!filename.getFrame(identifier).isSubsetOf(frame)) {
621                    set.add(identifier);
622                }
623            }
624        }
625        return set;
626    }
627 
628    public void setVariableBitRate(final boolean variableBitRate) {
629        this.variableBitRate = variableBitRate;
630    }
631 
632    public boolean isVariableBitRate() {
633        return variableBitRate;
634    }
635 
636    /**
637     * Adjust the lenght of the ID3v2 padding at the beginning of the MP3 file referred to in this object. The ID3v2
638     * size will be calculated, then a new file will be created with enough size to fit the <code>ID3v2</code> tag in
639     * this object. The old file will be deleted, and the new file renamed. All parameters will be taken from the
640     * <code>tagOptions</code> object.
641     *
642     * @throws FileNotFoundException if the file exists but is a directory rather than a regular file or cannot be
643     *                               opened for any other reason
644     * @throws IOException           on any I/O error
645     * @throws TagException          on any exception generated by this library.
646     */
647    public boolean adjustID3v2Padding() throws FileNotFoundException, IOException, TagException {
648        return adjustID3v2Padding(TagOptionSingleton.getInstance().getId3v2PaddingSize(),
649                                  TagOptionSingleton.getInstance().isId3v2PaddingWillShorten(),
650                                  TagOptionSingleton.getInstance().isId3v2PaddingCopyTag(),
651                                  mp3file);
652    }
653 
654    /**
655     * Adjust the length of the ID3v2 padding at the beginning of the MP3 file this object refers to. The ID3v2 size
656     * will be calculated, then a new file will be created with enough size to fit the <code>ID3v2</code> tag. The old
657     * file will be deleted, and the new file renamed.
658     *
659     * @param paddingSize  Initial padding size. This size is doubled until the ID3v2 tag will fit.
660     * @param willShorten  if the newly calculated padding size is less than the padding length of the file, then news
661     *                     the new shorter padding size if this is true.
662     * @param copyID3v2Tag if true, write the <code>ID3v2</code> tag of this object into the file
663     *
664     * @throws FileNotFoundException if the file exists but is a directory rather than a regular file or cannot be
665     *                               opened for any other reason
666     * @throws IOException           on any I/O error
667     * @throws TagException          on any exception generated by this library.
668     */
669    public boolean adjustID3v2Padding(final int paddingSize, final boolean willShorten, final boolean copyID3v2Tag)
670            throws FileNotFoundException, IOException, TagException {
671        return adjustID3v2Padding(paddingSize, willShorten, copyID3v2Tag, mp3file);
672    }
673 
674    /**
675     * Adjust the length of the ID3v2 padding at the beginning of the MP3 file this object refers to. The ID3v2 size
676     * will be calculated, then a new file will be created with enough size to fit the <code>ID3v2</code> tag. The old
677     * file will be deleted, and the new file renamed.
678     *
679     * @param paddingSize  Initial padding size. This size is doubled until the ID3v2 tag will fit. A paddingSize of
680     *                     zero will create a padding length exactly equal to the tag size.
681     * @param willShorten  Shorten the padding size by halves if the ID3v2 tag will fit
682     * @param copyID3v2Tag if true, write the <code>ID3v2</code> tag of this object into the file
683     * @param file         The file to adjust the padding length of
684     *
685     * @throws FileNotFoundException if the file exists but is a directory rather than a regular file or cannot be
686     *                               opened for any other reason
687     * @throws IOException           on any I/O error
688     * @throws TagException          on any exception generated by this library.
689     */
690    public boolean adjustID3v2Padding(final int paddingSize,
691                                      final boolean willShorten,
692                                      final boolean copyID3v2Tag,
693                                      final File file) throws FileNotFoundException, IOException, TagException {
694        int id3v2TagSize = 0;
695        final long mp3start = getMp3StartByte(file);
696        long newPaddingSize = paddingSize;
697        FileOutputStream outStream = null;
698        FileInputStream inStream = null;
699        File backupFile = null;
700        File paddedFile = null;
701        if (newPaddingSize < 0) {
702            throw new TagException("Invalid paddingSize: " + newPaddingSize);
703        }
704        if (hasID3v2Tag()) {
705            id3v2TagSize = getID3v2Tag().getSize();
706        }
707        if (newPaddingSize != 0) {
708            // double padding size until it's large enough
709            while (newPaddingSize < id3v2TagSize) {
710                newPaddingSize *= TagOptionSingleton.getInstance().getId3v2PaddingMultiplier();
711            }
712        }
713        if (newPaddingSize < mp3start && !willShorten) {
714            return false;
715        }
716        if (newPaddingSize == mp3start) {
717            return false;
718        }
719        try {
720            // we first copy everything to a new file, then replace the original
721            paddedFile = File.createTempFile("temp", ".mp3", file.getParentFile());
722            outStream = new FileOutputStream(paddedFile);
723            inStream = new FileInputStream(file);
724            byte[] buffer;
725            if (copyID3v2Tag == true) {
726                // paddingSize < mp3start && willshorten == false
727                // was already checked for outside of the try block.
728                if ((newPaddingSize < mp3start) && willShorten) {
729                    // copy the current tag
730                    buffer = new byte[(int) newPaddingSize];
731                    inStream.read(buffer, 0, buffer.length);
732                    outStream.write(buffer, 0, buffer.length);
733                    buffer = new byte[(int) (mp3start - newPaddingSize)];
734 
735                    // skip the rest of the tag that didn't fit
736                    inStream.read(buffer, 0, buffer.length);
737 
738                    // paddingSize > mp3start
739                } else {
740                    // copy the current tag
741                    buffer = new byte[(int) mp3start];
742                    inStream.read(buffer, 0, buffer.length);
743                    outStream.write(buffer, 0, buffer.length);
744 
745                    // add zeros for the rest of the padding
746                    if (newPaddingSize - mp3start > 0) {
747                        buffer = new byte[(int) (newPaddingSize - mp3start)];
748                        outStream.write(buffer, 0, buffer.length);
749                    }
750                }
751            } else {
752                buffer = new byte[(int) newPaddingSize];
753 
754                // skip the tag
755                inStream.skip(mp3start);
756 
757                // write zeros for the tag
758                outStream.write(buffer, 0, buffer.length);
759            }
760            buffer = new byte[1024];
761            int b = inStream.read(buffer, 0, buffer.length);
762            while (b == 1024) {
763                outStream.write(buffer, 0, buffer.length);
764                b = inStream.read(buffer, 0, buffer.length);
765            }
766            if (b != -1) {
767                outStream.write(buffer, 0, b);
768            }
769            backupFile = new File(file.getParentFile(), TagUtility.appendBeforeExtension(file.getName(), ".original"));
770            TagUtility.copyFile(file, backupFile);
771            if (backupFile.exists()) {
772                backupFile.setLastModified(file.lastModified());
773            } else {
774                return false;
775            }
776            TagUtility.copyFile(paddedFile, file);
777            return true;
778        } finally {
779            if (inStream != null) {
780                inStream.getFD().sync();
781                inStream.close();
782            }
783            if (outStream != null) {
784                outStream.getFD().sync();
785                outStream.close();
786            }
787            if ((backupFile != null) &&
788                (TagOptionSingleton.getInstance().isOriginalSavedAfterAdjustingID3v2Padding() == false)) {
789                backupFile.delete();
790            }
791            if (paddedFile != null) {
792                paddedFile.delete();
793            }
794        }
795    }
796 
797    public void delete(final AbstractMP3Tag mp3tag) throws FileNotFoundException, IOException {
798        mp3tag.delete(new RandomAccessFile(mp3file, "rw"));
799    }
800 
801    /**
802     * Returns true if this object contains an filename pseudo-tag
803     *
804     * @return true if this object contains an filename pseudo-tag
805     */
806    public boolean hasFilenameTag() {
807        return (filenameTag != null);
808    }
809 
810    /**
811     * Returns true if this object contains an <code>Id3v1</code> tag
812     *
813     * @return true if this object contains an <code>Id3v1</code> tag
814     */
815    public boolean hasID3v1Tag() {
816        return (id3v1tag != null);
817    }
818 
819    /**
820     * Returns true if this object contains an <code>Id3v2</code> tag
821     *
822     * @return true if this object contains an <code>Id3v2</code> tag
823     */
824    public boolean hasID3v2Tag() {
825        return (id3v2tag != null);
826    }
827 
828    /**
829     * Returns true if this object contains an <code>Lyrics3</code> tag
830     *
831     * @return true if this object contains an <code>Lyrics3</code> tag
832     */
833    public boolean hasLyrics3Tag() {
834        return (lyrics3tag != null);
835    }
836 
837    /**
838     * Saves the tags in this object to the file referred to by this object. It will be saved as
839     * TagConstants.MP3_FILE_SAVE_WRITE
840     *
841     * @throws IOException  on any I/O error
842     * @throws TagException on any exception generated by this library.
843     */
844    public void save() throws IOException, TagException {
845        save(mp3file, TagOptionSingleton.getInstance().getDefaultSaveMode());
846    }
847 
848    /**
849     * Saves the tags in this object to the file referred to by this object. It will be saved as
850     * TagConstants.MP3_FILE_SAVE_WRITE
851     *
852     * @param saveMode write, overwrite, or append. Defined as <code>TagConstants.MP3_FILE_SAVE_WRITE
853     *                 TagConstants.MP3_FILE_SAVE_OVERWRITE TagConstants.MP3_FILE_SAVE_APPEND </code>
854     *
855     * @throws IOException  on any I/O error
856     * @throws TagException on any exception generated by this library.
857     */
858    public void save(final int saveMode) throws IOException, TagException {
859        save(mp3file, saveMode);
860    }
861 
862    /**
863     * Saves the tags in this object to the file argument. It will be saved as TagConstants.MP3_FILE_SAVE_WRITE
864     *
865     * @param filename file to save the this object's tags to
866     *
867     * @throws IOException  on any I/O error
868     * @throws TagException on any exception generated by this library.
869     */
870    public void save(final String filename) throws IOException, TagException {
871        save(new File(filename), TagOptionSingleton.getInstance().getDefaultSaveMode());
872    }
873 
874    /**
875     * Saves the tags in this object to the file argument. It will be saved as TagConstants.MP3_FILE_SAVE_WRITE
876     *
877     * @param file file to save the this object's tags to
878     *
879     * @throws IOException  on any I/O error
880     * @throws TagException on any exception generated by this library.
881     */
882    public void save(final File file) throws IOException, TagException {
883        save(file, TagOptionSingleton.getInstance().getDefaultSaveMode());
884    }
885 
886    /**
887     * Saves the tags in this object to the file argument. It will be saved as TagConstants.MP3_FILE_SAVE_WRITE
888     *
889     * @param filename file to save the this object's tags to
890     * @param saveMode write, overwrite, or append. Defined as <code>TagConstants.MP3_FILE_SAVE_WRITE
891     *                 TagConstants.MP3_FILE_SAVE_OVERWRITE TagConstants.MP3_FILE_SAVE_APPEND </code>
892     *
893     * @throws IOException  on any I/O error
894     * @throws TagException on any exception generated by this library.
895     */
896    public void save(final String filename, final int saveMode) throws IOException, TagException {
897        save(new File(filename), saveMode);
898    }
899 
900    /**
901     * Saves the tags in this object to the file argument. It will be saved as TagConstants.MP3_FILE_SAVE_WRITE
902     *
903     * @param file     file to save the this object's tags to
904     * @param saveMode write, overwrite, or append. Defined as <code>TagConstants.MP3_FILE_SAVE_WRITE
905     *                 TagConstants.MP3_FILE_SAVE_OVERWRITE TagConstants.MP3_FILE_SAVE_APPEND </code>
906     *
907     * @throws IOException  on any I/O error
908     * @throws TagException on any exception generated by this library.
909     */
910    public void save(final File file, final int saveMode) throws IOException, TagException {
911        if ((saveMode < TagConstant.MP3_FILE_SAVE_FIRST) || (saveMode > TagConstant.MP3_FILE_SAVE_LAST)) {
912            throw new TagException("Invalid Save Mode");
913        }
914        RandomAccessFile rfile = null;
915        try {
916            if (id3v2tag != null) {
917                adjustID3v2Padding(TagOptionSingleton.getInstance().getId3v2PaddingSize(),
918                                   TagOptionSingleton.getInstance().isId3v2PaddingWillShorten(),
919                                   TagOptionSingleton.getInstance().isId3v2PaddingCopyTag(),
920                                   file);
921            }
922 
923            // we can't put these two if's together because
924            // adjustid3v2padding needs all handles on the file closed;
925            rfile = new RandomAccessFile(file, "rw");
926            if (TagOptionSingleton.getInstance().isId3v2Save()) {
927                if (id3v2tag == null) {
928                    if (saveMode == TagConstant.MP3_FILE_SAVE_OVERWRITE) {
929                        (new ID3v2_4()).delete(rfile);
930                    }
931                } else {
932                    if (saveMode == TagConstant.MP3_FILE_SAVE_WRITE) {
933                        id3v2tag.write(rfile);
934                    } else if (saveMode == TagConstant.MP3_FILE_SAVE_APPEND) {
935                        id3v2tag.append(rfile);
936                    } else if (saveMode == TagConstant.MP3_FILE_SAVE_OVERWRITE) {
937                        id3v2tag.overwrite(rfile);
938                    }
939                }
940            }
941            if (TagOptionSingleton.getInstance().isLyrics3Save()) {
942                if (lyrics3tag == null) {
943                    if (saveMode == TagConstant.MP3_FILE_SAVE_OVERWRITE) {
944                        (new Lyrics3v2()).delete(rfile);
945                    }
946                } else {
947                    if (saveMode == TagConstant.MP3_FILE_SAVE_WRITE) {
948                        lyrics3tag.write(rfile);
949                    } else if (saveMode == TagConstant.MP3_FILE_SAVE_APPEND) {
950                        lyrics3tag.append(rfile);
951                    } else if (saveMode == TagConstant.MP3_FILE_SAVE_OVERWRITE) {
952                        lyrics3tag.overwrite(rfile);
953                    }
954                }
955            }
956            if (TagOptionSingleton.getInstance().isId3v1Save()) {
957                if (id3v1tag == null) {
958                    if (saveMode == TagConstant.MP3_FILE_SAVE_OVERWRITE) {
959                        (new ID3v1()).delete(rfile);
960                    }
961                } else {
962                    if (saveMode == TagConstant.MP3_FILE_SAVE_WRITE) {
963                        id3v1tag.write(rfile);
964                    } else if (saveMode == TagConstant.MP3_FILE_SAVE_APPEND) {
965                        id3v1tag.append(rfile);
966                    } else if (saveMode == TagConstant.MP3_FILE_SAVE_OVERWRITE) {
967                        id3v1tag.overwrite(rfile);
968                    }
969                }
970            }
971            if (TagOptionSingleton.getInstance().isFilenameTagSave()) {
972                if (filenameTag != null) {
973                    if (saveMode == TagConstant.MP3_FILE_SAVE_WRITE) {
974                        filenameTag.write(rfile);
975                    } else if (saveMode == TagConstant.MP3_FILE_SAVE_APPEND) {
976                        filenameTag.append(rfile);
977                    } else if (saveMode == TagConstant.MP3_FILE_SAVE_OVERWRITE) {
978                        filenameTag.overwrite(rfile);
979                    }
980                }
981            }
982        } finally {
983            if (rfile != null) {
984                rfile.close();
985            }
986        }
987    }
988 
989    /**
990     * Returns true if the first MP3 frame can be found for the MP3 file that this object refers to. This is the first
991     * byte of music data and not the ID3 Tag Frame.
992     *
993     * @return true if the first MP3 frame can be found
994     *
995     * @throws IOException on any I/O error
996     */
997    public boolean seekMP3Frame() throws IOException {
998        RandomAccessFile rfile = null;
999        boolean found = false;
1000        try {
1001            rfile = new RandomAccessFile(mp3file, "r");
1002            found = seekMP3Frame(rfile);
1003        } finally {
1004            if (rfile != null) {
1005                rfile.close();
1006            }
1007        }
1008        return found;
1009    }
1010 
1011    /**
1012     * Returns true if the first MP3 frame can be found for the MP3 file argument. It tries to sync as many frame as
1013     * defined in <code>TagOptions.getNumberMP3SyncFrame</code> This is the first byte of music data and not the ID3 Tag
1014     * Frame.
1015     *
1016     * @param seekFile MP3 file to seek
1017     *
1018     * @return true if the first MP3 frame can be found
1019     *
1020     * @throws IOException on any I/O error
1021     */
1022    public boolean seekMP3Frame(final RandomAccessFile seekFile) throws IOException {
1023        boolean syncFound = false;
1024        byte first;
1025        byte second;
1026        long filePointer = 1;
1027        variableBitRate = false;
1028        try {
1029            seekFile.seek(0);
1030            do {
1031                first = seekFile.readByte();
1032                if (first == (byte) 0xFF) {
1033                    filePointer = seekFile.getFilePointer();
1034                    second = (byte) (seekFile.readByte() & (byte) 0xE0);
1035                    if (second == (byte) 0xE0) {
1036                        seekFile.seek(filePointer - 1);
1037 
1038                        // seek the next frames, recursively
1039                        syncFound = seekNextMP3Frame(seekFile,
1040                                                     TagOptionSingleton.getInstance().getNumberMP3SyncFrame());
1041                    }
1042                    seekFile.seek(filePointer);
1043                }
1044            } while (syncFound == false);
1045            seekFile.seek(filePointer - 1);
1046        } catch (EOFException ex) {
1047            syncFound = false;
1048        } catch (IOException ex) {
1049            throw ex;
1050        }
1051        return syncFound;
1052    }
1053 
1054    /**
1055     * Returns the MP3 frame size for the file this object refers to. It assumes that <code>seekNextMP3Frame</code> has
1056     * already been called.
1057     *
1058     * @return MP3 Frame size in bytes.
1059     */
1060    private int getFrameSize() {
1061        if (frequency == 0) {
1062            return 0;
1063        }
1064        final int size;
1065        final int paddingByte = padding ? 1 : 0;
1066        if (layer == 3) { // Layer I
1067            size = (int) ((((12 * bitRate) / frequency) + paddingByte) * 4);
1068        } else {
1069            size = (int) (((144 * bitRate) / frequency) + paddingByte);
1070        }
1071        return size;
1072    }
1073 
1074    /**
1075     * Reads the mp3 frame header from the current posiiton in the file and sets this object's private variables to what
1076     * is found. It assumes the <code>RandomAccessFile</code> is already pointing to a valid MP3 Frame.
1077     *
1078     * @param file File to read frame header
1079     *
1080     * @throws IOException          on any I/O error
1081     * @throws TagNotFoundException if MP3 Frame sync bites were not immediately found
1082     * @throws InvalidTagException  if any of the header values are invlaid
1083     */
1084    private void readFrameHeader(final RandomAccessFile file)
1085            throws IOException, TagNotFoundException, InvalidTagException {
1086        final byte[] buffer = new byte[4];
1087        file.read(buffer);
1088 
1089        // sync
1090        if ((buffer[0] != (byte) 0xFF) || ((buffer[1] & (byte) 0xE0) != (byte) 0xE0)) {
1091            throw new TagNotFoundException("MP3 Frame sync bits not found");
1092        }
1093        mpegVersion = (byte) ((buffer[1] & TagConstant.MASK_MP3_VERSION) >> 3);
1094        layer = (byte) ((buffer[1] & TagConstant.MASK_MP3_LAYER) >> 1);
1095        protection = (buffer[1] & TagConstant.MASK_MP3_PROTECTION) != 1;
1096        final int bitRateValue = (buffer[2] & TagConstant.MASK_MP3_BITRATE) |
1097                                 (buffer[1] & TagConstant.MASK_MP3_ID) |
1098                                 (buffer[1] & TagConstant.MASK_MP3_LAYER);
1099        final Long object = (Long) TagConstant.bitrate.get(new Long(bitRateValue));
1100        if (object != null) {
1101            if (object.longValue() != bitRate) {
1102                variableBitRate = true;
1103            }
1104            bitRate = object.intValue();
1105        } else {
1106            throw new InvalidTagException("Invalid bit rate");
1107        }
1108        final int frequencyValue = (buffer[2] & TagConstant.MASK_MP3_FREQUENCY) >>> 2;
1109        if (mpegVersion == 3) { // Version 1.0
1110            switch (frequencyValue) {
1111                case 0:
1112                    frequency = 44.1;
1113                    break;
1114                case 1:
1115                    frequency = 48.0;
1116                    break;
1117                case 2:
1118                    frequency = 32.0;
1119                    break;
1120            }
1121        } else if (mpegVersion == 2) { // Version 2.0
1122            switch (frequencyValue) {
1123                case 0:
1124                    frequency = 22.05;
1125                    break;
1126                case 1:
1127                    frequency = 24.00;
1128                    break;
1129                case 2:
1130                    frequency = 16.00;
1131                    break;
1132            }
1133        } else if (mpegVersion == 00) { // Version 2.5
1134            switch (frequencyValue) {
1135                case 0:
1136                    frequency = 11.025;
1137                    break;
1138                case 1:
1139                    frequency = 12.00;
1140                    break;
1141                case 2:
1142                    frequency = 8.00;
1143                    break;
1144            }
1145        } else {
1146            throw new InvalidTagException("Invalid MPEG version");
1147        }
1148        padding = (buffer[2] & TagConstant.MASK_MP3_PADDING) != 0;
1149        privacy = (buffer[2] & TagConstant.MASK_MP3_PRIVACY) != 0;
1150        mode = (byte) ((buffer[3] & TagConstant.MASK_MP3_MODE) >> 6);
1151        modeExtension = (byte) ((buffer[3] & TagConstant.MASK_MP3_MODE_EXTENSION) >> 4);
1152        copyProtected = (buffer[3] & TagConstant.MASK_MP3_COPY) != 0;
1153        home = (buffer[3] & TagConstant.MASK_MP3_HOME) != 0;
1154        emphasis = (byte) ((buffer[3] & TagConstant.MASK_MP3_EMPHASIS));
1155    }
1156 
1157    /**
1158     * Returns true if the first MP3 frame can be found for the MP3 file argument. It is recursive and called by
1159     * seekMP3Frame. This is the first byte of music data and not the ID3 Tag Frame.
1160     *
1161     * @param file       MP3 file to seek
1162     * @param iterations recursive counter
1163     *
1164     * @return true if the first MP3 frame can be found
1165     *
1166     * @throws IOException on any I/O error
1167     */
1168    private boolean seekNextMP3Frame(final RandomAccessFile file, final int iterations) throws IOException {
1169        final boolean syncFound;
1170        final byte[] buffer;
1171        final byte first;
1172        final byte second;
1173        final long filePointer;
1174        if (iterations == 0) {
1175            syncFound = true;
1176        } else {
1177            try {
1178                readFrameHeader(file);
1179            } catch (TagException ex) {
1180                return false;
1181            }
1182            final int size = getFrameSize();
1183            if ((size <= 0) || (size > file.length())) {
1184                return false;
1185            }
1186            buffer = new byte[size - 4];
1187            file.read(buffer);
1188            filePointer = file.getFilePointer();
1189            first = file.readByte();
1190            if (first == (byte) 0xFF) {
1191                second = (byte) (file.readByte() & (byte) 0xE0);
1192                if (second == (byte) 0xE0) {
1193                    file.seek(filePointer);
1194 
1195                    // recursively find the next frames
1196                    syncFound = seekNextMP3Frame(file, iterations - 1);
1197                } else {
1198                    syncFound = false;
1199                }
1200            } else {
1201                syncFound = false;
1202            }
1203        }
1204        return syncFound;
1205    }
1206}

[all classes][org.farng.mp3]
EMMA 2.0.5312 (C) Vladimir Roubtsov