summaryrefslogtreecommitdiff
path: root/songdbj/javazoom/spi/mpeg/sampled
diff options
context:
space:
mode:
authorBjörn Stenberg <bjorn@haxx.se>2007-01-08 23:53:00 +0000
committerBjörn Stenberg <bjorn@haxx.se>2007-01-08 23:53:00 +0000
commit7039a05147b8bbfc829babea1c65bd436450b505 (patch)
tree4ba555eb84ed97b72b0575034d5b0530a393713e /songdbj/javazoom/spi/mpeg/sampled
parent6d4c19707ef95942e323cbdc89fbbfdbe45e7cc5 (diff)
downloadrockbox-7039a05147b8bbfc829babea1c65bd436450b505.tar.gz
rockbox-7039a05147b8bbfc829babea1c65bd436450b505.zip
Splitting out songdbj
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@11953 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'songdbj/javazoom/spi/mpeg/sampled')
-rw-r--r--songdbj/javazoom/spi/mpeg/sampled/convert/DecodedMpegAudioInputStream.java334
-rw-r--r--songdbj/javazoom/spi/mpeg/sampled/convert/MpegFormatConversionProvider.java120
-rw-r--r--songdbj/javazoom/spi/mpeg/sampled/file/IcyListener.java131
-rw-r--r--songdbj/javazoom/spi/mpeg/sampled/file/MpegAudioFileFormat.java103
-rw-r--r--songdbj/javazoom/spi/mpeg/sampled/file/MpegAudioFileReader.java772
-rw-r--r--songdbj/javazoom/spi/mpeg/sampled/file/MpegAudioFormat.java67
-rw-r--r--songdbj/javazoom/spi/mpeg/sampled/file/MpegEncoding.java47
-rw-r--r--songdbj/javazoom/spi/mpeg/sampled/file/MpegFileFormatType.java40
-rw-r--r--songdbj/javazoom/spi/mpeg/sampled/file/tag/IcyInputStream.java412
-rw-r--r--songdbj/javazoom/spi/mpeg/sampled/file/tag/IcyTag.java42
-rw-r--r--songdbj/javazoom/spi/mpeg/sampled/file/tag/MP3MetadataParser.java50
-rw-r--r--songdbj/javazoom/spi/mpeg/sampled/file/tag/MP3Tag.java52
-rw-r--r--songdbj/javazoom/spi/mpeg/sampled/file/tag/MP3TagParseSupport.java62
-rw-r--r--songdbj/javazoom/spi/mpeg/sampled/file/tag/StringableTag.java36
-rw-r--r--songdbj/javazoom/spi/mpeg/sampled/file/tag/TagParseEvent.java44
-rw-r--r--songdbj/javazoom/spi/mpeg/sampled/file/tag/TagParseListener.java37
16 files changed, 0 insertions, 2349 deletions
diff --git a/songdbj/javazoom/spi/mpeg/sampled/convert/DecodedMpegAudioInputStream.java b/songdbj/javazoom/spi/mpeg/sampled/convert/DecodedMpegAudioInputStream.java
deleted file mode 100644
index e4e6fed00a..0000000000
--- a/songdbj/javazoom/spi/mpeg/sampled/convert/DecodedMpegAudioInputStream.java
+++ /dev/null
@@ -1,334 +0,0 @@
1/*
2 * DecodedMpegAudioInputStream.
3 *
4 * JavaZOOM : mp3spi@javazoom.net
5 * http://www.javazoom.net
6 *
7 *-----------------------------------------------------------------------------
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Library General Public License as published
10 * by the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public
19 * License along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 *------------------------------------------------------------------------
22 */
23
24package javazoom.spi.mpeg.sampled.convert;
25import java.io.IOException;
26import java.io.InputStream;
27import java.util.HashMap;
28import java.util.Map;
29
30import javax.sound.sampled.AudioFormat;
31import javax.sound.sampled.AudioInputStream;
32
33import javazoom.jl.decoder.Bitstream;
34import javazoom.jl.decoder.BitstreamException;
35import javazoom.jl.decoder.Decoder;
36import javazoom.jl.decoder.DecoderException;
37import javazoom.jl.decoder.Equalizer;
38import javazoom.jl.decoder.Header;
39import javazoom.jl.decoder.Obuffer;
40import javazoom.spi.PropertiesContainer;
41import javazoom.spi.mpeg.sampled.file.IcyListener;
42import javazoom.spi.mpeg.sampled.file.tag.TagParseEvent;
43import javazoom.spi.mpeg.sampled.file.tag.TagParseListener;
44
45import org.tritonus.share.TDebug;
46import org.tritonus.share.sampled.convert.TAsynchronousFilteredAudioInputStream;
47
48/**
49 * Main decoder.
50 */
51public class DecodedMpegAudioInputStream extends TAsynchronousFilteredAudioInputStream implements PropertiesContainer, TagParseListener
52{
53 private InputStream m_encodedStream;
54 private Bitstream m_bitstream;
55 private Decoder m_decoder;
56 private Equalizer m_equalizer;
57 private float[] m_equalizer_values;
58 private Header m_header;
59 private DMAISObuffer m_oBuffer;
60
61 // Bytes info.
62 private long byteslength = -1;
63 private long currentByte = 0;
64 // Frame info.
65 private int frameslength = -1;
66 private long currentFrame = 0;
67 private int currentFramesize = 0;
68 private int currentBitrate = -1;
69 // Time info.
70 private long currentMicrosecond = 0;
71 // Shoutcast stream info
72 private IcyListener shoutlst = null;
73
74
75 private HashMap properties = null;
76
77 public DecodedMpegAudioInputStream(AudioFormat outputFormat, AudioInputStream inputStream)
78 {
79 super(outputFormat, -1);
80 if (TDebug.TraceAudioConverter)
81 {
82 TDebug.out(">DecodedMpegAudioInputStream(AudioFormat outputFormat, AudioInputStream inputStream)");
83 }
84 try
85 {
86 // Try to find out inputstream length to allow skip.
87 byteslength = inputStream.available();
88 }
89 catch (IOException e)
90 {
91 TDebug.out("DecodedMpegAudioInputStream : Cannot run inputStream.available() : "+e.getMessage());
92 byteslength = -1;
93 }
94 m_encodedStream = inputStream;
95 shoutlst = IcyListener.getInstance();
96 shoutlst.reset();
97 m_bitstream = new Bitstream(inputStream);
98 m_decoder = new Decoder(null);
99 m_equalizer = new Equalizer();
100 m_equalizer_values = new float[32];
101 for (int b=0;b<m_equalizer.getBandCount();b++)
102 {
103 m_equalizer_values[b] = m_equalizer.getBand(b);
104 }
105 m_decoder.setEqualizer(m_equalizer);
106 m_oBuffer = new DMAISObuffer(outputFormat.getChannels());
107 m_decoder.setOutputBuffer(m_oBuffer);
108 try
109 {
110 m_header = m_bitstream.readFrame();
111 if ((m_header != null) && (frameslength == -1) && (byteslength > 0)) frameslength = m_header.max_number_of_frames((int)byteslength);
112 }
113 catch (BitstreamException e)
114 {
115 TDebug.out("DecodedMpegAudioInputStream : Cannot read first frame : "+e.getMessage());
116 byteslength = -1;
117 }
118 properties = new HashMap();
119 }
120
121 /**
122 * Return dynamic properties.
123 *
124 * <ul>
125 * <li><b>mp3.frame</b> [Long], current frame position.
126 * <li><b>mp3.frame.bitrate</b> [Integer], bitrate of the current frame.
127 * <li><b>mp3.frame.size.bytes</b> [Integer], size in bytes of the current frame.
128 * <li><b>mp3.position.byte</b> [Long], current position in bytes in the stream.
129 * <li><b>mp3.position.microseconds</b> [Long], elapsed microseconds.
130 * <li><b>mp3.equalizer</b> float[32], interactive equalizer array, values could be in [-1.0, +1.0].
131 * <li><b>mp3.shoutcast.metadata.key</b> [String], Shoutcast meta key with matching value.
132 * <br>For instance :
133 * <br>mp3.shoutcast.metadata.StreamTitle=Current song playing in stream.
134 * <br>mp3.shoutcast.metadata.StreamUrl=Url info.
135 * </ul>
136 */
137 public Map properties()
138 {
139 properties.put("mp3.frame",new Long(currentFrame));
140 properties.put("mp3.frame.bitrate",new Integer(currentBitrate));
141 properties.put("mp3.frame.size.bytes",new Integer(currentFramesize));
142 properties.put("mp3.position.byte",new Long(currentByte));
143 properties.put("mp3.position.microseconds",new Long(currentMicrosecond));
144 properties.put("mp3.equalizer",m_equalizer_values);
145 // Optionnal shoutcast stream meta-data.
146 if (shoutlst != null)
147 {
148 String surl = shoutlst.getStreamUrl();
149 String stitle = shoutlst.getStreamTitle();
150 if ((stitle != null) && (stitle.trim().length()>0)) properties.put("mp3.shoutcast.metadata.StreamTitle",stitle);
151 if ((surl != null) && (surl.trim().length()>0)) properties.put("mp3.shoutcast.metadata.StreamUrl",surl);
152 }
153 return properties;
154 }
155
156 public void execute()
157 {
158 if (TDebug.TraceAudioConverter) TDebug.out("execute() : begin");
159 try
160 {
161 // Following line hangs when FrameSize is available in AudioFormat.
162 Header header = null;
163 if (m_header == null) header = m_bitstream.readFrame();
164 else header = m_header;
165 if (TDebug.TraceAudioConverter) TDebug.out("execute() : header = "+header);
166 if (header == null)
167 {
168 if (TDebug.TraceAudioConverter)
169 {
170 TDebug.out("header is null (end of mpeg stream)");
171 }
172 getCircularBuffer().close();
173 return;
174 }
175 currentFrame++;
176 currentBitrate = header.bitrate_instant();
177 currentFramesize = header.calculate_framesize();
178 currentByte = currentByte + currentFramesize;
179 currentMicrosecond = (long) (currentFrame* header.ms_per_frame()*1000.0f);
180 for (int b=0;b<m_equalizer_values.length;b++)
181 {
182 m_equalizer.setBand(b,m_equalizer_values[b]);
183 }
184 m_decoder.setEqualizer(m_equalizer);
185 Obuffer decoderOutput = m_decoder.decodeFrame(header, m_bitstream);
186 m_bitstream.closeFrame();
187 getCircularBuffer().write(m_oBuffer.getBuffer(), 0, m_oBuffer.getCurrentBufferSize());
188 m_oBuffer.reset();
189 if (m_header != null) m_header = null;
190 }
191 catch (BitstreamException e)
192 {
193 if (TDebug.TraceAudioConverter)
194 {
195 TDebug.out(e);
196 }
197 }
198 catch (DecoderException e)
199 {
200 if (TDebug.TraceAudioConverter)
201 {
202 TDebug.out(e);
203 }
204 }
205 if (TDebug.TraceAudioConverter) TDebug.out("execute() : end");
206 }
207
208 public long skip(long bytes)
209 {
210 if ((byteslength > 0) && (frameslength > 0))
211 {
212 float ratio = bytes*1.0f/byteslength*1.0f;
213 long bytesread = skipFrames((long) (ratio*frameslength));
214 currentByte = currentByte + bytesread;
215 return bytesread;
216 }
217 else return -1;
218 }
219
220 /**
221 * Skip frames.
222 * You don't need to call it severals times, it will exactly skip given frames number.
223 * @param frames
224 * @return bytes length skipped matching to frames skipped.
225 */
226 public long skipFrames(long frames)
227 {
228 if (TDebug.TraceAudioConverter) TDebug.out("skip(long frames) : begin");
229 int framesRead = 0;
230 int bytesReads = 0;
231 try
232 {
233 for (int i=0;i<frames;i++)
234 {
235 Header header = m_bitstream.readFrame();
236 if (header != null)
237 {
238 int fsize = header.calculate_framesize();
239 bytesReads = bytesReads + fsize;
240 }
241 m_bitstream.closeFrame();
242 framesRead++;
243 }
244 }
245 catch (BitstreamException e)
246 {
247 if (TDebug.TraceAudioConverter) TDebug.out(e);
248 }
249 if (TDebug.TraceAudioConverter) TDebug.out("skip(long frames) : end");
250 currentFrame = currentFrame + framesRead;
251 return bytesReads;
252 }
253
254 private boolean isBigEndian()
255 {
256 return getFormat().isBigEndian();
257 }
258
259 public void close() throws IOException
260 {
261 super.close();
262 m_encodedStream.close();
263 }
264
265 private class DMAISObuffer extends Obuffer
266 {
267 private int m_nChannels;
268 private byte[] m_abBuffer;
269 private int[] m_anBufferPointers;
270 private boolean m_bIsBigEndian;
271 public DMAISObuffer(int nChannels)
272 {
273 m_nChannels = nChannels;
274 m_abBuffer = new byte[OBUFFERSIZE * nChannels];
275 m_anBufferPointers = new int[nChannels];
276 reset();
277 m_bIsBigEndian = DecodedMpegAudioInputStream.this.isBigEndian();
278 }
279 public void append(int nChannel, short sValue)
280 {
281 byte bFirstByte;
282 byte bSecondByte;
283 if (m_bIsBigEndian)
284 {
285 bFirstByte = (byte) ((sValue >>> 8) & 0xFF);
286 bSecondByte = (byte) (sValue & 0xFF);
287 }
288 else // little endian
289 {
290 bFirstByte = (byte) (sValue & 0xFF);
291 bSecondByte = (byte) ((sValue >>> 8) & 0xFF);
292 }
293 m_abBuffer[m_anBufferPointers[nChannel]] = bFirstByte;
294 m_abBuffer[m_anBufferPointers[nChannel] + 1] = bSecondByte;
295 m_anBufferPointers[nChannel] += m_nChannels * 2;
296 }
297 public void set_stop_flag()
298 {
299 }
300 public void close()
301 {
302 }
303 public void write_buffer(int nValue)
304 {
305 }
306 public void clear_buffer()
307 {
308 }
309 public byte[] getBuffer()
310 {
311 return m_abBuffer;
312 }
313 public int getCurrentBufferSize()
314 {
315 return m_anBufferPointers[0];
316 }
317 public void reset()
318 {
319 for (int i = 0; i < m_nChannels; i++)
320 {
321 /* Points to byte location,
322 * implicitely assuming 16 bit
323 * samples.
324 */
325 m_anBufferPointers[i] = i * 2;
326 }
327 }
328 }
329
330 public void tagParsed(TagParseEvent tpe)
331 {
332 System.out.println("TAG:"+tpe.getTag());
333 }
334}
diff --git a/songdbj/javazoom/spi/mpeg/sampled/convert/MpegFormatConversionProvider.java b/songdbj/javazoom/spi/mpeg/sampled/convert/MpegFormatConversionProvider.java
deleted file mode 100644
index 1a3d51d9ad..0000000000
--- a/songdbj/javazoom/spi/mpeg/sampled/convert/MpegFormatConversionProvider.java
+++ /dev/null
@@ -1,120 +0,0 @@
1/*
2 * MpegFormatConversionProvider.
3 *
4 * JavaZOOM : mp3spi@javazoom.net
5 * http://www.javazoom.net
6 *
7 * ---------------------------------------------------------------------------
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Library General Public License as published
10 * by the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public
19 * License along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 * --------------------------------------------------------------------------
22 */
23
24
25package javazoom.spi.mpeg.sampled.convert;
26
27
28import java.util.Arrays;
29
30import javax.sound.sampled.AudioFormat;
31import javax.sound.sampled.AudioInputStream;
32import javax.sound.sampled.AudioSystem;
33
34import javazoom.spi.mpeg.sampled.file.MpegEncoding;
35
36import org.tritonus.share.TDebug;
37import org.tritonus.share.sampled.Encodings;
38import org.tritonus.share.sampled.convert.TEncodingFormatConversionProvider;
39
40/**
41 * ConversionProvider for MPEG files.
42 */
43public class MpegFormatConversionProvider extends TEncodingFormatConversionProvider
44{
45 private static final AudioFormat.Encoding MP3 = Encodings.getEncoding("MP3");
46 private static final AudioFormat.Encoding PCM_SIGNED = Encodings.getEncoding("PCM_SIGNED");
47
48 private static final AudioFormat[] INPUT_FORMATS =
49 {
50 // mono
51 new AudioFormat(MP3, -1.0F, -1, 1, -1, -1.0F, false),
52 new AudioFormat(MP3, -1.0F, -1, 1, -1, -1.0F, true),
53 // stereo
54 new AudioFormat(MP3, -1.0F, -1, 2, -1, -1.0F, false),
55 new AudioFormat(MP3, -1.0F, -1, 2, -1, -1.0F, true),
56 };
57
58
59 private static final AudioFormat[] OUTPUT_FORMATS =
60 {
61 // mono, 16 bit signed
62 new AudioFormat(PCM_SIGNED, -1.0F, 16, 1, 2, -1.0F, false),
63 new AudioFormat(PCM_SIGNED, -1.0F, 16, 1, 2, -1.0F, true),
64 // stereo, 16 bit signed
65 new AudioFormat(PCM_SIGNED, -1.0F, 16, 2, 4, -1.0F, false),
66 new AudioFormat(PCM_SIGNED, -1.0F, 16, 2, 4, -1.0F, true),
67 };
68
69 /**
70 * Constructor.
71 */
72 public MpegFormatConversionProvider()
73 {
74 super(Arrays.asList(INPUT_FORMATS), Arrays.asList(OUTPUT_FORMATS));
75 if (TDebug.TraceAudioConverter)
76 {
77 TDebug.out(">MpegFormatConversionProvider()");
78 }
79 }
80
81 public AudioInputStream getAudioInputStream(AudioFormat targetFormat, AudioInputStream audioInputStream)
82 {
83 if (TDebug.TraceAudioConverter)
84 {
85 TDebug.out(">MpegFormatConversionProvider.getAudioInputStream(AudioFormat targetFormat, AudioInputStream audioInputStream):");
86 }
87 return new DecodedMpegAudioInputStream(targetFormat, audioInputStream);
88 }
89
90 /**
91 * Add conversion support for any MpegEncoding source with FrameRate or FrameSize not empty.
92 * @param targetFormat
93 * @param sourceFormat
94 * @return
95 */
96 public boolean isConversionSupported(AudioFormat targetFormat, AudioFormat sourceFormat)
97 {
98 if (TDebug.TraceAudioConverter)
99 {
100 TDebug.out(">MpegFormatConversionProvider.isConversionSupported(AudioFormat targetFormat, AudioFormat sourceFormat):");
101 TDebug.out("checking if conversion possible");
102 TDebug.out("from: " + sourceFormat);
103 TDebug.out("to: " + targetFormat);
104 }
105
106 boolean conversion = super.isConversionSupported(targetFormat, sourceFormat);
107 if (conversion == false)
108 {
109 AudioFormat.Encoding enc = sourceFormat.getEncoding();
110 if (enc instanceof MpegEncoding)
111 {
112 if ((sourceFormat.getFrameRate() != AudioSystem.NOT_SPECIFIED) || (sourceFormat.getFrameSize() != AudioSystem.NOT_SPECIFIED))
113 {
114 conversion = true;
115 }
116 }
117 }
118 return conversion;
119 }
120}
diff --git a/songdbj/javazoom/spi/mpeg/sampled/file/IcyListener.java b/songdbj/javazoom/spi/mpeg/sampled/file/IcyListener.java
deleted file mode 100644
index 8e49e6e2ef..0000000000
--- a/songdbj/javazoom/spi/mpeg/sampled/file/IcyListener.java
+++ /dev/null
@@ -1,131 +0,0 @@
1/*
2 * IcyListener.
3 *
4 * JavaZOOM : mp3spi@javazoom.net
5 * http://www.javazoom.net
6 *
7 *-----------------------------------------------------------------------
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Library General Public License as published
10 * by the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public
19 * License along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 *----------------------------------------------------------------------
22 */
23
24package javazoom.spi.mpeg.sampled.file;
25
26import javazoom.spi.mpeg.sampled.file.tag.MP3Tag;
27import javazoom.spi.mpeg.sampled.file.tag.TagParseEvent;
28import javazoom.spi.mpeg.sampled.file.tag.TagParseListener;
29
30/**
31 * This class (singleton) allow to be notified on shoutcast meta data
32 * while playing the stream (such as song title).
33 */
34public class IcyListener implements TagParseListener
35{
36 private static IcyListener instance = null;
37 private MP3Tag lastTag = null;
38 private String streamTitle = null;
39 private String streamUrl = null;
40
41
42 private IcyListener()
43 {
44 super();
45 }
46
47 public static synchronized IcyListener getInstance()
48 {
49 if (instance == null)
50 {
51 instance = new IcyListener();
52 }
53 return instance;
54 }
55
56 /* (non-Javadoc)
57 * @see javazoom.spi.mpeg.sampled.file.tag.TagParseListener#tagParsed(javazoom.spi.mpeg.sampled.file.tag.TagParseEvent)
58 */
59 public void tagParsed(TagParseEvent tpe)
60 {
61 lastTag = tpe.getTag();
62 String name = lastTag.getName();
63 if ((name != null) && (name.equalsIgnoreCase("streamtitle")))
64 {
65 streamTitle = (String) lastTag.getValue();
66 }
67 else if ((name != null) && (name.equalsIgnoreCase("streamurl")))
68 {
69 streamUrl = (String) lastTag.getValue();
70 }
71 }
72
73 /**
74 * @return
75 */
76 public MP3Tag getLastTag()
77 {
78 return lastTag;
79 }
80
81 /**
82 * @param tag
83 */
84 public void setLastTag(MP3Tag tag)
85 {
86 lastTag = tag;
87 }
88
89 /**
90 * @return
91 */
92 public String getStreamTitle()
93 {
94 return streamTitle;
95 }
96
97 /**
98 * @return
99 */
100 public String getStreamUrl()
101 {
102 return streamUrl;
103 }
104
105 /**
106 * @param string
107 */
108 public void setStreamTitle(String string)
109 {
110 streamTitle = string;
111 }
112
113 /**
114 * @param string
115 */
116 public void setStreamUrl(String string)
117 {
118 streamUrl = string;
119 }
120
121 /**
122 * Reset all properties.
123 */
124 public void reset()
125 {
126 lastTag = null;
127 streamTitle = null;
128 streamUrl = null;
129 }
130
131}
diff --git a/songdbj/javazoom/spi/mpeg/sampled/file/MpegAudioFileFormat.java b/songdbj/javazoom/spi/mpeg/sampled/file/MpegAudioFileFormat.java
deleted file mode 100644
index afdc4c5e9c..0000000000
--- a/songdbj/javazoom/spi/mpeg/sampled/file/MpegAudioFileFormat.java
+++ /dev/null
@@ -1,103 +0,0 @@
1/*
2 * MpegAudioFileFormat.
3 *
4 * JavaZOOM : mp3spi@javazoom.net
5 * http://www.javazoom.net
6 *
7 *-----------------------------------------------------------------------
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Library General Public License as published
10 * by the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public
19 * License along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 *----------------------------------------------------------------------
22 */
23package javazoom.spi.mpeg.sampled.file;
24
25import java.util.Map;
26
27import javax.sound.sampled.AudioFormat;
28
29import org.tritonus.share.sampled.file.TAudioFileFormat;
30
31/**
32 * @author JavaZOOM
33 */
34public class MpegAudioFileFormat extends TAudioFileFormat
35{
36 /**
37 * Contructor.
38 * @param type
39 * @param audioFormat
40 * @param nLengthInFrames
41 * @param nLengthInBytes
42 */
43 public MpegAudioFileFormat(Type type, AudioFormat audioFormat, int nLengthInFrames, int nLengthInBytes, Map properties)
44 {
45 super(type, audioFormat, nLengthInFrames, nLengthInBytes, properties);
46 }
47
48 /**
49 * MP3 audio file format parameters.
50 * Some parameters might be unavailable. So availability test is required before reading any parameter.
51 *
52 * <br>AudioFileFormat parameters.
53 * <ul>
54 * <li><b>duration</b> [Long], duration in microseconds.
55 * <li><b>title</b> [String], Title of the stream.
56 * <li><b>author</b> [String], Name of the artist of the stream.
57 * <li><b>album</b> [String], Name of the album of the stream.
58 * <li><b>date</b> [String], The date (year) of the recording or release of the stream.
59 * <li><b>copyright</b> [String], Copyright message of the stream.
60 * <li><b>comment</b> [String], Comment of the stream.
61 * </ul>
62 * <br>MP3 parameters.
63 * <ul>
64 * <li><b>mp3.version.mpeg</b> [String], mpeg version : 1,2 or 2.5
65 * <li><b>mp3.version.layer</b> [String], layer version 1, 2 or 3
66 * <li><b>mp3.version.encoding</b> [String], mpeg encoding : MPEG1, MPEG2-LSF, MPEG2.5-LSF
67 * <li><b>mp3.channels</b> [Integer], number of channels 1 : mono, 2 : stereo.
68 * <li><b>mp3.frequency.hz</b> [Integer], sampling rate in hz.
69 * <li><b>mp3.bitrate.nominal.bps</b> [Integer], nominal bitrate in bps.
70 * <li><b>mp3.length.bytes</b> [Integer], length in bytes.
71 * <li><b>mp3.length.frames</b> [Integer], length in frames.
72 * <li><b>mp3.framesize.bytes</b> [Integer], framesize of the first frame. framesize is not constant for VBR streams.
73 * <li><b>mp3.framerate.fps</b> [Float], framerate in frames per seconds.
74 * <li><b>mp3.header.pos</b> [Integer], position of first audio header (or ID3v2 size).
75 * <li><b>mp3.vbr</b> [Boolean], vbr flag.
76 * <li><b>mp3.vbr.scale</b> [Integer], vbr scale.
77 * <li><b>mp3.crc</b> [Boolean], crc flag.
78 * <li><b>mp3.original</b> [Boolean], original flag.
79 * <li><b>mp3.copyright</b> [Boolean], copyright flag.
80 * <li><b>mp3.padding</b> [Boolean], padding flag.
81 * <li><b>mp3.mode</b> [Integer], mode 0:STEREO 1:JOINT_STEREO 2:DUAL_CHANNEL 3:SINGLE_CHANNEL
82 * <li><b>mp3.id3tag.genre</b> [String], ID3 tag (v1 or v2) genre.
83 * <li><b>mp3.id3tag.track</b> [String], ID3 tag (v1 or v2) track info.
84 * <li><b>mp3.id3tag.encoded</b> [String], ID3 tag v2 encoded by info.
85 * <li><b>mp3.id3tag.composer</b> [String], ID3 tag v2 composer info.
86 * <li><b>mp3.id3tag.grouping</b> [String], ID3 tag v2 grouping info.
87 * <li><b>mp3.id3tag.disc</b> [String], ID3 tag v2 track info.
88 * <li><b>mp3.id3tag.v2</b> [InputStream], ID3v2 frames.
89 * <li><b>mp3.id3tag.v2.version</b> [String], ID3v2 major version (2=v2.2.0, 3=v2.3.0, 4=v2.4.0).
90 * <li><b>mp3.shoutcast.metadata.key</b> [String], Shoutcast meta key with matching value.
91 * <br>For instance :
92 * <br>mp3.shoutcast.metadata.icy-irc=#shoutcast
93 * <br>mp3.shoutcast.metadata.icy-metaint=8192
94 * <br>mp3.shoutcast.metadata.icy-genre=Trance Techno Dance
95 * <br>mp3.shoutcast.metadata.icy-url=http://www.di.fm
96 * <br>and so on ...
97 * </ul>
98 */
99 public Map properties()
100 {
101 return super.properties();
102 }
103}
diff --git a/songdbj/javazoom/spi/mpeg/sampled/file/MpegAudioFileReader.java b/songdbj/javazoom/spi/mpeg/sampled/file/MpegAudioFileReader.java
deleted file mode 100644
index 54440551a1..0000000000
--- a/songdbj/javazoom/spi/mpeg/sampled/file/MpegAudioFileReader.java
+++ /dev/null
@@ -1,772 +0,0 @@
1/*
2 * MpegAudioFileReader.
3 *
4 * 12/31/04 : mp3spi.weak system property added to skip controls.
5 *
6 * 11/29/04 : ID3v2.2, v2.3 & v2.4 support improved.
7 * "mp3.id3tag.composer" (TCOM/TCM) added
8 * "mp3.id3tag.grouping" (TIT1/TT1) added
9 * "mp3.id3tag.disc" (TPA/TPOS) added
10 * "mp3.id3tag.encoded" (TEN/TENC) added
11 * "mp3.id3tag.v2.version" added
12 *
13 * 11/28/04 : String encoding bug fix in chopSubstring method.
14 *
15 * JavaZOOM : mp3spi@javazoom.net
16 * http://www.javazoom.net
17 *
18 *-----------------------------------------------------------------------
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU Library General Public License as published
21 * by the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
23 *
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU Library General Public License for more details.
28 *
29 * You should have received a copy of the GNU Library General Public
30 * License along with this program; if not, write to the Free Software
31 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
32 *----------------------------------------------------------------------
33 */
34
35package javazoom.spi.mpeg.sampled.file;
36
37
38import java.io.BufferedInputStream;
39import java.io.File;
40import java.io.FileInputStream;
41import java.io.IOException;
42import java.io.InputStream;
43import java.io.PushbackInputStream;
44import java.io.UnsupportedEncodingException;
45import java.net.URL;
46import java.net.URLConnection;
47import java.security.AccessControlException;
48import java.util.HashMap;
49
50import javax.sound.sampled.AudioFileFormat;
51import javax.sound.sampled.AudioFormat;
52import javax.sound.sampled.AudioInputStream;
53import javax.sound.sampled.AudioSystem;
54import javax.sound.sampled.UnsupportedAudioFileException;
55
56import javazoom.jl.decoder.Bitstream;
57import javazoom.jl.decoder.Header;
58import javazoom.spi.mpeg.sampled.file.tag.IcyInputStream;
59import javazoom.spi.mpeg.sampled.file.tag.MP3Tag;
60
61import org.tritonus.share.TDebug;
62import org.tritonus.share.sampled.file.TAudioFileReader;
63
64/**
65 * This class implements AudioFileReader for MP3 SPI.
66 */
67public class MpegAudioFileReader extends TAudioFileReader
68{
69 private final int SYNC = 0xFFE00000;
70 private String weak = null;
71 private final AudioFormat.Encoding[][] sm_aEncodings =
72 {
73 {MpegEncoding.MPEG2L1, MpegEncoding.MPEG2L2, MpegEncoding.MPEG2L3},
74 {MpegEncoding.MPEG1L1, MpegEncoding.MPEG1L2, MpegEncoding.MPEG1L3},
75 {MpegEncoding.MPEG2DOT5L1, MpegEncoding.MPEG2DOT5L2, MpegEncoding.MPEG2DOT5L3},
76
77 };
78
79 private static final int INITAL_READ_LENGTH = 64000;
80 private static final int MARK_LIMIT = INITAL_READ_LENGTH + 1;
81
82 private static final String[] id3v1genres = {
83 "Blues"
84 , "Classic Rock"
85 , "Country"
86 , "Dance"
87 , "Disco"
88 , "Funk"
89 , "Grunge"
90 , "Hip-Hop"
91 , "Jazz"
92 , "Metal"
93 , "New Age"
94 , "Oldies"
95 , "Other"
96 , "Pop"
97 , "R&B"
98 , "Rap"
99 , "Reggae"
100 , "Rock"
101 , "Techno"
102 , "Industrial"
103 , "Alternative"
104 , "Ska"
105 , "Death Metal"
106 , "Pranks"
107 , "Soundtrack"
108 , "Euro-Techno"
109 , "Ambient"
110 , "Trip-Hop"
111 , "Vocal"
112 , "Jazz+Funk"
113 , "Fusion"
114 , "Trance"
115 , "Classical"
116 , "Instrumental"
117 , "Acid"
118 , "House"
119 , "Game"
120 , "Sound Clip"
121 , "Gospel"
122 , "Noise"
123 , "AlternRock"
124 , "Bass"
125 , "Soul"
126 , "Punk"
127 , "Space"
128 , "Meditative"
129 , "Instrumental Pop"
130 , "Instrumental Rock"
131 , "Ethnic"
132 , "Gothic"
133 , "Darkwave"
134 , "Techno-Industrial"
135 , "Electronic"
136 , "Pop-Folk"
137 , "Eurodance"
138 , "Dream"
139 , "Southern Rock"
140 , "Comedy"
141 , "Cult"
142 , "Gangsta"
143 , "Top 40"
144 , "Christian Rap"
145 , "Pop/Funk"
146 , "Jungle"
147 , "Native American"
148 , "Cabaret"
149 , "New Wave"
150 , "Psychadelic"
151 , "Rave"
152 , "Showtunes"
153 , "Trailer"
154 , "Lo-Fi"
155 , "Tribal"
156 , "Acid Punk"
157 , "Acid Jazz"
158 , "Polka"
159 , "Retro"
160 , "Musical"
161 , "Rock & Roll"
162 , "Hard Rock"
163 , "Folk"
164 , "Folk-Rock"
165 , "National Folk"
166 , "Swing"
167 , "Fast Fusion"
168 , "Bebob"
169 , "Latin"
170 , "Revival"
171 , "Celtic"
172 , "Bluegrass"
173 , "Avantgarde"
174 , "Gothic Rock"
175 , "Progressive Rock"
176 , "Psychedelic Rock"
177 , "Symphonic Rock"
178 , "Slow Rock"
179 , "Big Band"
180 , "Chorus"
181 , "Easy Listening"
182 , "Acoustic"
183 , "Humour"
184 , "Speech"
185 , "Chanson"
186 , "Opera"
187 , "Chamber Music"
188 , "Sonata"
189 , "Symphony"
190 , "Booty Brass"
191 , "Primus"
192 , "Porn Groove"
193 , "Satire"
194 , "Slow Jam"
195 , "Club"
196 , "Tango"
197 , "Samba"
198 , "Folklore"
199 , "Ballad"
200 , "Power Ballad"
201 , "Rhythmic Soul"
202 , "Freestyle"
203 , "Duet"
204 , "Punk Rock"
205 , "Drum Solo"
206 , "A Capela"
207 , "Euro-House"
208 , "Dance Hall"
209 , "Goa"
210 , "Drum & Bass"
211 , "Club-House"
212 , "Hardcore"
213 , "Terror"
214 , "Indie"
215 , "BritPop"
216 , "Negerpunk"
217 , "Polsk Punk"
218 , "Beat"
219 , "Christian Gangsta Rap"
220 , "Heavy Metal"
221 , "Black Metal"
222 , "Crossover"
223 , "Contemporary Christian"
224 , "Christian Rock"
225 , "Merengue"
226 , "Salsa"
227 , "Thrash Metal"
228 , "Anime"
229 , "JPop"
230 , "SynthPop"
231 };
232
233 public MpegAudioFileReader()
234 {
235 super(MARK_LIMIT, true);
236 if (TDebug.TraceAudioFileReader) TDebug.out(">MpegAudioFileReader(1.9.2-FINAL)");
237 try
238 {
239 weak = System.getProperty("mp3spi.weak");
240 }
241 catch(AccessControlException e)
242 {}
243 }
244
245 /**
246 * Returns AudioFileFormat from File.
247 */
248 public AudioFileFormat getAudioFileFormat(File file) throws UnsupportedAudioFileException, IOException
249 {
250 return super.getAudioFileFormat(file);
251 }
252
253 /**
254 * Returns AudioFileFormat from URL.
255 */
256 public AudioFileFormat getAudioFileFormat(URL url) throws UnsupportedAudioFileException, IOException
257 {
258 if (TDebug.TraceAudioFileReader) {TDebug.out("MpegAudioFileReader.getAudioFileFormat(URL): begin"); }
259 long lFileLengthInBytes = AudioSystem.NOT_SPECIFIED;
260 URLConnection conn = url.openConnection();
261 // Tell shoucast server (if any) that SPI support shoutcast stream.
262 conn.setRequestProperty ("Icy-Metadata", "1");
263 InputStream inputStream = conn.getInputStream();
264 AudioFileFormat audioFileFormat = null;
265 try
266 {
267 audioFileFormat = getAudioFileFormat(inputStream, lFileLengthInBytes);
268 }
269 finally
270 {
271 inputStream.close();
272 }
273 if (TDebug.TraceAudioFileReader) {TDebug.out("MpegAudioFileReader.getAudioFileFormat(URL): end"); }
274 return audioFileFormat;
275 }
276
277 /**
278 * Returns AudioFileFormat from inputstream and medialength.
279 */
280 public AudioFileFormat getAudioFileFormat(InputStream inputStream, long mediaLength) throws UnsupportedAudioFileException, IOException
281 {
282 if (TDebug.TraceAudioFileReader) TDebug.out(">MpegAudioFileReader.getAudioFileFormat(InputStream inputStream, long mediaLength): begin");
283 HashMap aff_properties = new HashMap();
284 HashMap af_properties = new HashMap();
285 int mLength = (int) mediaLength;
286 int size = inputStream.available();
287 PushbackInputStream pis = new PushbackInputStream(inputStream, MARK_LIMIT);
288 byte head[] = new byte[12];
289 pis.read(head);
290 if (TDebug.TraceAudioFileReader)
291 {
292 TDebug.out("InputStream : "+inputStream + " =>" + new String(head));
293 }
294 /*
295 * Check for WAV, AU, and AIFF file formats.
296 *
297 * Next check for Shoutcast (supported) and OGG (unsupported) streams.
298 *
299 * Note -- the check for WAV files will reject Broadcast WAV files.
300 * This may be incorrect as broadcast WAV files may contain MPEG data.
301 * Need to investigate.
302 *
303 */
304 if ((head[0] == 'R') && (head[1] == 'I') && (head[2] == 'F') && (head[3] == 'F') && (head[8] == 'W') && (head[9] == 'A') && (head[10] == 'V') && (head[11] == 'E'))
305 {
306 if (TDebug.TraceAudioFileReader) TDebug.out("WAV stream found");
307 if (weak == null) throw new UnsupportedAudioFileException("WAV stream found");
308 }
309 else if ((head[0] == '.') && (head[1] == 's') && (head[2] == 'n') && (head[3] == 'd'))
310 {
311 if (TDebug.TraceAudioFileReader) TDebug.out("AU stream found");
312 if (weak == null) throw new UnsupportedAudioFileException("AU stream found");
313 }
314 else if ((head[0] == 'F') && (head[1] == 'O') && (head[2] == 'R') && (head[3] == 'M') && (head[8] == 'A') && (head[9] == 'I') && (head[10] == 'F') && (head[11] == 'F'))
315 {
316 if (TDebug.TraceAudioFileReader) TDebug.out("AIFF stream found");
317 if (weak == null) throw new UnsupportedAudioFileException("AIFF stream found");
318 }
319 // Shoutcast stream ?
320 else if (((head[0] == 'I') | (head[0] == 'i')) && ((head[1] == 'C') | (head[1] == 'c')) && ((head[2] == 'Y') | (head[2] == 'y')))
321 {
322 pis.unread(head);
323 // Load shoutcast meta data.
324 loadShoutcastInfo(pis, aff_properties);
325 }
326 // Ogg stream ?
327 else if (((head[0] == 'O') | (head[0] == 'o')) && ((head[1] == 'G') | (head[1] == 'g')) && ((head[2] == 'G') | (head[2] == 'g')))
328 {
329 if (TDebug.TraceAudioFileReader) TDebug.out("Ogg stream found");
330 if (weak == null) throw new UnsupportedAudioFileException("Ogg stream found");
331 }
332 // No, so pushback.
333 else
334 {
335 pis.unread(head);
336 }
337 // MPEG header info.
338 int nVersion = AudioSystem.NOT_SPECIFIED;
339 int nLayer = AudioSystem.NOT_SPECIFIED;
340 int nSFIndex = AudioSystem.NOT_SPECIFIED;
341 int nMode = AudioSystem.NOT_SPECIFIED;
342 int FrameSize = AudioSystem.NOT_SPECIFIED;
343 int nFrameSize = AudioSystem.NOT_SPECIFIED;
344 int nFrequency = AudioSystem.NOT_SPECIFIED;
345 int nTotalFrames = AudioSystem.NOT_SPECIFIED;
346 float FrameRate = AudioSystem.NOT_SPECIFIED;
347 int BitRate = AudioSystem.NOT_SPECIFIED;
348 int nChannels = AudioSystem.NOT_SPECIFIED;
349 int nHeader = AudioSystem.NOT_SPECIFIED;
350 int nTotalMS = AudioSystem.NOT_SPECIFIED;
351 boolean nVBR = false;
352 AudioFormat.Encoding encoding = null;
353 try
354 {
355 Bitstream m_bitstream = new Bitstream(pis);
356 aff_properties.put("mp3.header.pos",new Integer(m_bitstream.header_pos()));
357 Header m_header = m_bitstream.readFrame();
358 // nVersion = 0 => MPEG2-LSF (Including MPEG2.5), nVersion = 1 => MPEG1
359 nVersion = m_header.version();
360 if (nVersion == 2) aff_properties.put("mp3.version.mpeg",Float.toString(2.5f));
361 else aff_properties.put("mp3.version.mpeg",Integer.toString(2-nVersion));
362 // nLayer = 1,2,3
363 nLayer = m_header.layer();
364 aff_properties.put("mp3.version.layer",Integer.toString(nLayer));
365 nSFIndex = m_header.sample_frequency();
366 nMode = m_header.mode();
367 aff_properties.put("mp3.mode",new Integer(nMode));
368 nChannels = nMode == 3 ? 1 : 2;
369 aff_properties.put("mp3.channels",new Integer(nChannels));
370 nVBR = m_header.vbr();
371 af_properties.put("vbr",new Boolean(nVBR));
372 aff_properties.put("mp3.vbr",new Boolean(nVBR));
373 aff_properties.put("mp3.vbr.scale",new Integer(m_header.vbr_scale()));
374 FrameSize = m_header.calculate_framesize();
375 aff_properties.put("mp3.framesize.bytes",new Integer(FrameSize));
376 if (FrameSize < 0) throw new UnsupportedAudioFileException("Invalid FrameSize : " + FrameSize);
377 nFrequency = m_header.frequency();
378 aff_properties.put("mp3.frequency.hz",new Integer(nFrequency));
379 FrameRate = (float) ((1.0 / (m_header.ms_per_frame())) * 1000.0);
380 aff_properties.put("mp3.framerate.fps",new Float(FrameRate));
381 if (FrameRate < 0) throw new UnsupportedAudioFileException("Invalid FrameRate : " + FrameRate);
382 if (mLength != AudioSystem.NOT_SPECIFIED)
383 {
384 aff_properties.put("mp3.length.bytes",new Integer(mLength));
385 nTotalFrames = m_header.max_number_of_frames(mLength);
386 aff_properties.put("mp3.length.frames",new Integer(nTotalFrames));
387 }
388 BitRate = m_header.bitrate();
389 af_properties.put("bitrate",new Integer(BitRate));
390 aff_properties.put("mp3.bitrate.nominal.bps",new Integer(BitRate));
391 nHeader = m_header.getSyncHeader();
392 encoding = sm_aEncodings[nVersion][nLayer - 1];
393 aff_properties.put("mp3.version.encoding",encoding.toString());
394 if (mLength != AudioSystem.NOT_SPECIFIED)
395 {
396 nTotalMS = Math.round(m_header.total_ms(mLength));
397 aff_properties.put("duration",new Long((long)nTotalMS*1000L));
398 }
399 aff_properties.put("mp3.copyright",new Boolean(m_header.copyright()));
400 aff_properties.put("mp3.original",new Boolean(m_header.original()));
401 aff_properties.put("mp3.crc",new Boolean(m_header.checksums()));
402 aff_properties.put("mp3.padding",new Boolean(m_header.padding()));
403 InputStream id3v2 = m_bitstream.getRawID3v2();
404 if (id3v2 != null)
405 {
406 aff_properties.put("mp3.id3tag.v2",id3v2);
407 parseID3v2Frames(id3v2,aff_properties);
408 }
409 if (TDebug.TraceAudioFileReader) TDebug.out(m_header.toString());
410 }
411 catch (Exception e)
412 {
413 if (TDebug.TraceAudioFileReader) TDebug.out("not a MPEG stream:" + e.getMessage());
414 throw new UnsupportedAudioFileException("not a MPEG stream:" + e.getMessage());
415 }
416
417 // Deeper checks ?
418 int cVersion = (nHeader >> 19) & 0x3;
419 if (cVersion == 1)
420 {
421 if (TDebug.TraceAudioFileReader) TDebug.out("not a MPEG stream: wrong version");
422 throw new UnsupportedAudioFileException("not a MPEG stream: wrong version");
423 }
424
425 int cSFIndex = (nHeader >> 10) & 0x3;
426 if (cSFIndex == 3)
427 {
428 if (TDebug.TraceAudioFileReader) TDebug.out("not a MPEG stream: wrong sampling rate");
429 throw new UnsupportedAudioFileException("not a MPEG stream: wrong sampling rate");
430 }
431
432 // Look up for ID3v1 tag
433 if ((size == mediaLength) && (mediaLength != AudioSystem.NOT_SPECIFIED))
434 {
435 FileInputStream fis = (FileInputStream) inputStream;
436 byte[] id3v1 = new byte[128];
437 long bytesSkipped = fis.skip(inputStream.available()-id3v1.length);
438 int read = fis.read(id3v1,0,id3v1.length);
439 if ((id3v1[0]=='T') && (id3v1[1]=='A') && (id3v1[2]=='G'))
440 {
441 parseID3v1Frames(id3v1, aff_properties);
442 }
443 }
444
445 AudioFormat format = new MpegAudioFormat(encoding, (float) nFrequency, AudioSystem.NOT_SPECIFIED // SampleSizeInBits - The size of a sample
446 , nChannels // Channels - The number of channels
447 , -1 // The number of bytes in each frame
448 , FrameRate // FrameRate - The number of frames played or recorded per second
449 , true
450 , af_properties);
451 return new MpegAudioFileFormat(MpegFileFormatType.MP3, format, nTotalFrames, mLength,aff_properties);
452 }
453
454 /**
455 * Returns AudioInputStream from file.
456 */
457 public AudioInputStream getAudioInputStream(File file) throws UnsupportedAudioFileException, IOException
458 {
459 if (TDebug.TraceAudioFileReader) TDebug.out("getAudioInputStream(File file)");
460 InputStream inputStream = new FileInputStream(file);
461 try
462 {
463 return getAudioInputStream(inputStream);
464 }
465 catch (UnsupportedAudioFileException e)
466 {
467 if (inputStream != null) inputStream.close();
468 throw e;
469 }
470 catch (IOException e)
471 {
472 if (inputStream != null) inputStream.close();
473 throw e;
474 }
475 }
476
477 /**
478 * Returns AudioInputStream from url.
479 */
480 public AudioInputStream getAudioInputStream(URL url)
481 throws UnsupportedAudioFileException, IOException
482 {
483 if (TDebug.TraceAudioFileReader) {TDebug.out("MpegAudioFileReader.getAudioInputStream(URL): begin"); }
484 long lFileLengthInBytes = AudioSystem.NOT_SPECIFIED;
485 URLConnection conn = url.openConnection();
486 // Tell shoucast server (if any) that SPI support shoutcast stream.
487 boolean isShout = false;
488 int toRead=4;
489 byte[] head = new byte[toRead];
490
491 conn.setRequestProperty ("Icy-Metadata", "1");
492 BufferedInputStream bInputStream = new BufferedInputStream(conn.getInputStream());
493 bInputStream.mark(toRead);
494 int read = bInputStream.read(head,0,toRead);
495 if ((read>2) && (((head[0] == 'I') | (head[0] == 'i')) && ((head[1] == 'C') | (head[1] == 'c')) && ((head[2] == 'Y') | (head[2] == 'y')))) isShout = true;
496 bInputStream.reset();
497 InputStream inputStream = null;
498 // Is is a shoutcast server ?
499 if (isShout == true)
500 {
501 // Yes
502 IcyInputStream icyStream = new IcyInputStream(bInputStream);
503 icyStream.addTagParseListener(IcyListener.getInstance());
504 inputStream = icyStream;
505 }
506 else
507 {
508 // No, is Icecast 2 ?
509 String metaint = conn.getHeaderField("icy-metaint");
510 if (metaint != null)
511 {
512 // Yes, it might be icecast 2 mp3 stream.
513 IcyInputStream icyStream = new IcyInputStream(bInputStream,metaint);
514 icyStream.addTagParseListener(IcyListener.getInstance());
515 inputStream = icyStream;
516 }
517 else
518 {
519 // No
520 inputStream = bInputStream;
521 }
522 }
523 AudioInputStream audioInputStream = null;
524 try
525 {
526 audioInputStream = getAudioInputStream(inputStream, lFileLengthInBytes);
527 }
528 catch (UnsupportedAudioFileException e)
529 {
530 inputStream.close();
531 throw e;
532 }
533 catch (IOException e)
534 {
535 inputStream.close();
536 throw e;
537 }
538 if (TDebug.TraceAudioFileReader) {TDebug.out("MpegAudioFileReader.getAudioInputStream(URL): end"); }
539 return audioInputStream;
540 }
541
542 /**
543 * Return the AudioInputStream from the given InputStream.
544 */
545 public AudioInputStream getAudioInputStream(InputStream inputStream) throws UnsupportedAudioFileException, IOException
546 {
547 if (TDebug.TraceAudioFileReader) TDebug.out("MpegAudioFileReader.getAudioInputStream(InputStream inputStream)");
548 if (!inputStream.markSupported()) inputStream = new BufferedInputStream(inputStream);
549 return super.getAudioInputStream(inputStream);
550 }
551
552 /**
553 * Parser ID3v1 frames
554 * @param frames
555 * @param props
556 */
557 protected void parseID3v1Frames(byte[] frames, HashMap props)
558 {
559 if (TDebug.TraceAudioFileReader) TDebug.out("Parsing ID3v1");
560 String tag = null;
561 try
562 {
563 tag = new String(frames, 0, frames.length, "ISO-8859-1");
564 }
565 catch (UnsupportedEncodingException e)
566 {
567 tag = new String(frames, 0, frames.length);
568 if (TDebug.TraceAudioFileReader) TDebug.out("Cannot use ISO-8859-1");
569 }
570 if (TDebug.TraceAudioFileReader) TDebug.out("ID3v1 frame dump='"+tag+"'");
571 int start = 3;
572 String titlev1 = chopSubstring(tag, start, start += 30);
573 String titlev2 = (String) props.get("title");
574 if (((titlev2==null) || (titlev2.length()==0)) && (titlev1 != null)) props.put("title",titlev1);
575 String artistv1 = chopSubstring(tag, start, start += 30);
576 String artistv2 = (String) props.get("author");
577 if (((artistv2==null) || (artistv2.length()==0)) && (artistv1 != null)) props.put("author",artistv1);
578 String albumv1 = chopSubstring(tag, start, start += 30);
579 String albumv2 = (String) props.get("album");
580 if (((albumv2==null) || (albumv2.length()==0)) && (albumv1 != null)) props.put("album",albumv1);
581 String yearv1 = chopSubstring(tag, start, start += 4);
582 String yearv2 = (String) props.get("year");
583 if (((yearv2==null) || (yearv2.length()==0)) && (yearv1 != null)) props.put("date",yearv1);
584 String commentv1 = chopSubstring(tag, start, start += 28);
585 String commentv2 = (String) props.get("comment");
586 if (((commentv2==null) || (commentv2.length()==0)) && (commentv1 != null)) props.put("comment",commentv1);
587 String trackv1 = ""+((int) (frames[126] & 0xff));
588 String trackv2 = (String) props.get("mp3.id3tag.track");
589 if (((trackv2==null) || (trackv2.length()==0)) && (trackv1 != null)) props.put("mp3.id3tag.track",trackv1);
590
591 int genrev1 = (int) (frames[127] & 0xff);
592 if ((genrev1 >=0) && (genrev1<id3v1genres.length))
593 {
594 String genrev2 = (String) props.get("mp3.id3tag.genre");
595 if (((genrev2==null) || (genrev2.length()==0))) props.put("mp3.id3tag.genre",id3v1genres[genrev1]);
596 }
597 if (TDebug.TraceAudioFileReader) TDebug.out("ID3v1 parsed");
598 }
599
600 /**
601 * Extract
602 * @param s
603 * @param start
604 * @param end
605 * @return
606 */
607 private String chopSubstring(String s, int start, int end)
608 {
609 String str = null;
610 // 11/28/04 - String encoding bug fix.
611 try
612 {
613 str = s.substring(start, end);
614 int loc = str.indexOf('\0');
615 if (loc != -1) str = str.substring(0, loc);
616 }
617 catch (StringIndexOutOfBoundsException e)
618 {
619 // Skip encoding issues.
620 if (TDebug.TraceAudioFileReader) TDebug.out("Cannot chopSubString "+e.getMessage());
621 }
622 return str;
623 }
624 /**
625 * Parse ID3v2 frames to add album (TALB), title (TIT2), date (TYER), author (TPE1), copyright (TCOP), comment (COMM) ...
626 * @param frames
627 * @param props
628 */
629 protected void parseID3v2Frames(InputStream frames, HashMap props)
630 {
631 if (TDebug.TraceAudioFileReader) TDebug.out("Parsing ID3v2");
632 byte[] bframes = null;
633 int size = -1;
634 try
635 {
636 size = frames.available();
637 bframes = new byte[size];
638 frames.mark(size);
639 frames.read(bframes);
640 frames.reset();
641 }
642 catch (IOException e)
643 {
644 if (TDebug.TraceAudioFileReader) TDebug.out("Cannot parse ID3v2 :"+e.getMessage());
645 }
646
647 try
648 {
649 if (TDebug.TraceAudioFileReader) TDebug.out("ID3v2 frame dump='"+new String(bframes,0,bframes.length)+"'");
650 /* ID3 tags : http://www.unixgods.org/~tilo/ID3/docs/ID3_comparison.html */
651 String value = null;
652 for (int i=0;i<bframes.length-4;i++)
653 {
654 String code = new String(bframes,i,4);
655 String scode = new String(bframes,i,3);
656 // ID3v2.3 & v2.4
657 if ((code.equals("TALB")) || (code.equals("TIT2")) || (code.equals("TYER")) || (code.equals("TPE1")) || (code.equals("TCOP")) || (code.equals("COMM")) || (code.equals("TCON")) || (code.equals("TRCK")) || (code.equals("TPOS")) || (code.equals("TDRC")) || (code.equals("TCOM")) || (code.equals("TIT1")) || (code.equals("TENC")))
658 {
659 i=i+10;
660 size = (int) (bframes[i-6] << 24) + (bframes[i-5] << 16) + (bframes[i-4] << 8) + (bframes[i-3]);
661 if (code.equals("COMM")) value = parseText(bframes, i, size, 5);
662 else value = parseText(bframes,i, size, 1);
663 if ((value != null) && (value.length()>0))
664 {
665 if (code.equals("TALB")) props.put("album",value);
666 else if (code.equals("TIT2")) props.put("title",value);
667 else if (code.equals("TYER")) props.put("date",value);
668 // ID3v2.4 date fix.
669 else if (code.equals("TDRC")) props.put("date",value);
670 else if (code.equals("TPE1")) props.put("author",value);
671 else if (code.equals("TCOP")) props.put("copyright",value);
672 else if (code.equals("COMM")) props.put("comment",value);
673 else if (code.equals("TCON")) props.put("mp3.id3tag.genre",value);
674 else if (code.equals("TRCK")) props.put("mp3.id3tag.track",value);
675 else if (code.equals("TPOS")) props.put("mp3.id3tag.disc",value);
676 else if (code.equals("TCOM")) props.put("mp3.id3tag.composer",value);
677 else if (code.equals("TIT1")) props.put("mp3.id3tag.grouping",value);
678 else if (code.equals("TENC")) props.put("mp3.id3tag.encoded",value);
679 }
680 i=i+size-1;
681 }
682 // ID3v2.2.
683 else if ((scode.equals("TAL")) || (scode.equals("TT2")) || (scode.equals("TP1")) || (scode.equals("TYE")) || (scode.equals("TRK")) || (scode.equals("TPA")) || (scode.equals("TCR")) || (scode.equals("TCO")) || (scode.equals("TCM")) || (scode.equals("COM")) || (scode.equals("TT1")) || (scode.equals("TEN")))
684 {
685 i=i+6;
686 size = (int) (0x00000000) + (bframes[i-3] << 16) + (bframes[i-2] << 8) + (bframes[i-1]);
687 if (scode.equals("COM")) value = parseText(bframes, i, size, 5);
688 else value = parseText(bframes,i, size, 1);
689 if ((value != null) && (value.length()>0))
690 {
691 if (scode.equals("TAL")) props.put("album",value);
692 else if (scode.equals("TT2")) props.put("title",value);
693 else if (scode.equals("TYE")) props.put("date",value);
694 else if (scode.equals("TP1")) props.put("author",value);
695 else if (scode.equals("TCR")) props.put("copyright",value);
696 else if (scode.equals("COM")) props.put("comment",value);
697 else if (scode.equals("TCO")) props.put("mp3.id3tag.genre",value);
698 else if (scode.equals("TRK")) props.put("mp3.id3tag.track",value);
699 else if (scode.equals("TPA")) props.put("mp3.id3tag.disc",value);
700 else if (scode.equals("TCM")) props.put("mp3.id3tag.composer",value);
701 else if (scode.equals("TT1")) props.put("mp3.id3tag.grouping",value);
702 else if (scode.equals("TEN")) props.put("mp3.id3tag.encoded",value);
703 }
704 i=i+size-1;
705 }
706 else if (code.startsWith("ID3"))
707 {
708 // ID3v2 Header.
709 int v2version = (int) (bframes[3] & 0xFF);
710 props.put("mp3.id3tag.v2.version",String.valueOf(v2version));
711 i=i+4;
712 }
713 }
714 }
715 catch (RuntimeException e)
716 {
717 // Ignore all parsing errors.
718 if (TDebug.TraceAudioFileReader) TDebug.out("Cannot parse ID3v2 :"+e.getMessage());
719 }
720 if (TDebug.TraceAudioFileReader) TDebug.out("ID3v2 parsed");
721 }
722
723 /**
724 * Parse Text Frames.
725 * @param bframes
726 * @param offset
727 * @param size
728 * @param skip
729 * @return
730 */
731 protected String parseText(byte[] bframes, int offset, int size, int skip)
732 {
733 String value = null;
734 try
735 {
736 String[] ENC_TYPES = {"ISO-8859-1", "UTF16","UTF-16BE", "UTF-8"};
737 value = new String(bframes,offset+skip,size-skip,ENC_TYPES[bframes[offset]]);
738 value = chopSubstring(value,0,value.length());
739 }
740 catch (UnsupportedEncodingException e)
741 {
742 if (TDebug.TraceAudioFileReader) TDebug.out("ID3v2 Encoding error :"+e.getMessage());
743 }
744 return value;
745 }
746
747 /**
748 * Load shoutcast (ICY) info.
749 * @param input
750 * @param props
751 * @throws IOException
752 */
753 protected void loadShoutcastInfo(InputStream input, HashMap props) throws IOException
754 {
755 IcyInputStream icy = new IcyInputStream(new BufferedInputStream(input));
756 HashMap metadata = icy.getTagHash();
757 MP3Tag titleMP3Tag = icy.getTag("icy-name");
758 if (titleMP3Tag != null) props.put("title",((String) titleMP3Tag.getValue()).trim());
759 MP3Tag[] meta = icy.getTags();
760 if (meta != null)
761 {
762 StringBuffer metaStr = new StringBuffer();
763 for (int i=0;i<meta.length;i++)
764 {
765 String key = meta[i].getName();
766 String value = ((String) icy.getTag(key).getValue()).trim();
767 props.put("mp3.shoutcast.metadata."+key, value);
768 }
769 }
770 }
771
772} \ No newline at end of file
diff --git a/songdbj/javazoom/spi/mpeg/sampled/file/MpegAudioFormat.java b/songdbj/javazoom/spi/mpeg/sampled/file/MpegAudioFormat.java
deleted file mode 100644
index 29a66a3d93..0000000000
--- a/songdbj/javazoom/spi/mpeg/sampled/file/MpegAudioFormat.java
+++ /dev/null
@@ -1,67 +0,0 @@
1/*
2 * MpegAudioFormat.
3 *
4 * JavaZOOM : mp3spi@javazoom.net
5 * http://www.javazoom.net
6 *
7 *-----------------------------------------------------------------------
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Library General Public License as published
10 * by the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public
19 * License along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 *----------------------------------------------------------------------
22 */
23
24package javazoom.spi.mpeg.sampled.file;
25
26import java.util.Map;
27
28import javax.sound.sampled.AudioFormat;
29
30import org.tritonus.share.sampled.TAudioFormat;
31
32/**
33 * @author JavaZOOM
34 */
35public class MpegAudioFormat extends TAudioFormat
36{
37 /**
38 * Constructor.
39 * @param encoding
40 * @param nFrequency
41 * @param SampleSizeInBits
42 * @param nChannels
43 * @param FrameSize
44 * @param FrameRate
45 * @param isBigEndian
46 * @param properties
47 */
48 public MpegAudioFormat(AudioFormat.Encoding encoding, float nFrequency, int SampleSizeInBits, int nChannels, int FrameSize, float FrameRate, boolean isBigEndian, Map properties)
49 {
50 super(encoding, nFrequency, SampleSizeInBits, nChannels, FrameSize, FrameRate, isBigEndian, properties);
51 }
52
53 /**
54 * MP3 audio format parameters.
55 * Some parameters might be unavailable. So availability test is required before reading any parameter.
56 *
57 * <br>AudioFormat parameters.
58 * <ul>
59 * <li><b>bitrate</b> [Integer], bitrate in bits per seconds, average bitrate for VBR enabled stream.
60 * <li><b>vbr</b> [Boolean], VBR flag.
61 * </ul>
62 */
63 public Map properties()
64 {
65 return super.properties();
66 }
67}
diff --git a/songdbj/javazoom/spi/mpeg/sampled/file/MpegEncoding.java b/songdbj/javazoom/spi/mpeg/sampled/file/MpegEncoding.java
deleted file mode 100644
index 6306d9ec80..0000000000
--- a/songdbj/javazoom/spi/mpeg/sampled/file/MpegEncoding.java
+++ /dev/null
@@ -1,47 +0,0 @@
1/*
2 * MpegEncoding.
3 *
4 * JavaZOOM : mp3spi@javazoom.net
5 * http://www.javazoom.net
6 *
7 *-----------------------------------------------------------------------
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Library General Public License as published
10 * by the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public
19 * License along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 *----------------------------------------------------------------------
22 */
23
24package javazoom.spi.mpeg.sampled.file;
25
26import javax.sound.sampled.AudioFormat;
27
28/**
29 * Encodings used by the MPEG audio decoder.
30 */
31public class MpegEncoding extends AudioFormat.Encoding
32{
33 public static final AudioFormat.Encoding MPEG1L1 = new MpegEncoding("MPEG1L1");
34 public static final AudioFormat.Encoding MPEG1L2 = new MpegEncoding("MPEG1L2");
35 public static final AudioFormat.Encoding MPEG1L3 = new MpegEncoding("MPEG1L3");
36 public static final AudioFormat.Encoding MPEG2L1 = new MpegEncoding("MPEG2L1");
37 public static final AudioFormat.Encoding MPEG2L2 = new MpegEncoding("MPEG2L2");
38 public static final AudioFormat.Encoding MPEG2L3 = new MpegEncoding("MPEG2L3");
39 public static final AudioFormat.Encoding MPEG2DOT5L1 = new MpegEncoding("MPEG2DOT5L1");
40 public static final AudioFormat.Encoding MPEG2DOT5L2 = new MpegEncoding("MPEG2DOT5L2");
41 public static final AudioFormat.Encoding MPEG2DOT5L3 = new MpegEncoding("MPEG2DOT5L3");
42
43 public MpegEncoding(String strName)
44 {
45 super(strName);
46 }
47}
diff --git a/songdbj/javazoom/spi/mpeg/sampled/file/MpegFileFormatType.java b/songdbj/javazoom/spi/mpeg/sampled/file/MpegFileFormatType.java
deleted file mode 100644
index 2c59ad8621..0000000000
--- a/songdbj/javazoom/spi/mpeg/sampled/file/MpegFileFormatType.java
+++ /dev/null
@@ -1,40 +0,0 @@
1/*
2 * MpegFileFormatType.
3 *
4 * JavaZOOM : mp3spi@javazoom.net
5 * http://www.javazoom.net
6 *
7 *-----------------------------------------------------------------------
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Library General Public License as published
10 * by the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public
19 * License along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 *----------------------------------------------------------------------
22 */
23
24package javazoom.spi.mpeg.sampled.file;
25
26import javax.sound.sampled.AudioFileFormat;
27
28/**
29 * FileFormatTypes used by the MPEG audio decoder.
30 */
31public class MpegFileFormatType extends AudioFileFormat.Type
32{
33 public static final AudioFileFormat.Type MPEG = new MpegFileFormatType("MPEG", "mpeg");
34 public static final AudioFileFormat.Type MP3 = new MpegFileFormatType("MP3", "mp3");
35
36 public MpegFileFormatType(String strName, String strExtension)
37 {
38 super(strName, strExtension);
39 }
40}
diff --git a/songdbj/javazoom/spi/mpeg/sampled/file/tag/IcyInputStream.java b/songdbj/javazoom/spi/mpeg/sampled/file/tag/IcyInputStream.java
deleted file mode 100644
index 22aa4439fe..0000000000
--- a/songdbj/javazoom/spi/mpeg/sampled/file/tag/IcyInputStream.java
+++ /dev/null
@@ -1,412 +0,0 @@
1/*
2 * IcyInputStream.
3 *
4 * jicyshout : http://sourceforge.net/projects/jicyshout/
5 *
6 * JavaZOOM : mp3spi@javazoom.net
7 * http://www.javazoom.net
8 *
9 *-----------------------------------------------------------------------
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU Library General Public License as published
12 * by the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Library General Public License for more details.
19 *
20 * You should have received a copy of the GNU Library General Public
21 * License along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 *----------------------------------------------------------------------
24 */
25
26package javazoom.spi.mpeg.sampled.file.tag;
27
28import java.io.BufferedInputStream;
29import java.io.IOException;
30import java.io.InputStream;
31import java.net.URL;
32import java.net.URLConnection;
33import java.util.HashMap;
34import java.util.StringTokenizer;
35
36/** An BufferedInputStream that parses Shoutcast's "icy" metadata
37 from the stream. Gets headers at the beginning and if the
38 "icy-metaint" tag is found, it parses and strips in-stream
39 metadata.
40 <p>
41 <b>The deal with metaint</b>: Icy streams don't try to put
42 tags between MP3 frames the way that ID3 does. Instead, it
43 requires the client to strip metadata from the stream before
44 it hits the decoder. You get an
45 <code>icy-metaint</code> name/val in the beginning of the
46 stream iff you sent "Icy-Metadata" with value "1" in the
47 request headers (SimpleMP3DataSource does this if the
48 "parseStreamMetadata" boolean is true). If this is the case
49 then the value of icy-metaint is the amount of real data
50 between metadata blocks. Each block begins with an int
51 indicating how much metadata there is -- the block is this
52 value times 16 (it can be, and often is, 0).
53 <p>
54 Originally thought that "icy" implied Icecast, but this is
55 completely wrong -- real Icecast servers, found through
56 www.icecast.net and typified by URLs with a trailing directory
57 (like CalArts School of Music - http://65.165.174.100:8000/som)
58 do not have the "ICY 200 OK" magic string or any of the
59 CRLF-separated headers. Apparently, "icy" means "Shoutcast".
60 Yep, that's weird.
61 @author Chris Adamson, invalidname@mac.com
62 */
63public class IcyInputStream
64 extends BufferedInputStream
65 implements MP3MetadataParser {
66
67 public static boolean DEBUG = false;
68
69 MP3TagParseSupport tagParseSupport;
70 /** inline tags are delimited by ';', also filter out
71 null bytes
72 */
73 protected static final String INLINE_TAG_SEPARATORS = ";\u0000";
74 /* looks like icy streams start start with
75 ICY 200 OK\r\n
76 then the tags are like
77 icy-notice1:<BR>This stream requires <a href="http://www.winamp.com/">Winamp</a><BR>\r\n
78 icy-notice2:SHOUTcast Distributed Network Audio Server/win32 v1.8.2<BR>\r\n
79 icy-name:Core-upt Radio\r\n
80 icy-genre:Punk Ska Emo\r\n
81 icy-url:http://www.core-uptrecords.com\r\n
82 icy-pub:1\r\n
83 icy-metaint:8192\r\n
84 icy-br:56\r\n
85 \r\n (signifies end of headers)
86 we only get icy-metaint if the http request that created
87 this stream sent the header "icy-metadata:1"
88 //
89 in in-line metadata, we read a byte that tells us how
90 many 16-byte blocks there are (presumably, we still use
91 \r\n for the separator... the block is padded out with
92 0x00's that we can ignore)
93
94 // when server is full/down/etc, we get the following for
95 // one of the notice lines:
96 icy-notice2:This server has reached its user limit<BR>
97 or
98 icy-notice2:The resource requested is currently unavailable<BR>
99 */
100 /** Tags that have been discovered in the stream.
101 */
102 HashMap tags;
103 /** Buffer for readCRLF line... note this limits lines to
104 1024 chars (I've read that WinAmp barfs at 128, so
105 this is generous)
106 */
107 protected byte[] crlfBuffer = new byte[1024];
108 /** value of the "metaint" tag, which tells us how many bytes
109 of real data are between the metadata tags. if -1, this stream
110 does not have metadata after the header.
111 */
112 protected int metaint = -1;
113 /** how many bytes of real data remain before the next
114 block of metadata. Only meaningful if metaint != -1.
115 */
116 protected int bytesUntilNextMetadata = -1;
117 // TODO: comment for constructor
118 /** Reads the initial headers of the stream and adds
119 tags appropriatly. Gets set up to find, read,
120 and strip blocks of in-line metadata if the
121 <code>icy-metaint</code> header is found.
122 */
123 public IcyInputStream(InputStream in) throws IOException {
124 super(in);
125 tags = new HashMap();
126 tagParseSupport = new MP3TagParseSupport();
127 // read the initial tags here, including the metaint
128 // and set the counter for how far we read until
129 // the next metadata block (if any).
130 readInitialHeaders();
131 IcyTag metaIntTag = (IcyTag) getTag("icy-metaint");
132 if (DEBUG) System.out.println("METATAG:"+metaIntTag);
133 if (metaIntTag != null) {
134 String metaIntString = metaIntTag.getValueAsString();
135 try {
136 metaint = Integer.parseInt(metaIntString.trim());
137 if (DEBUG) System.out.println("METAINT:"+metaint);
138 bytesUntilNextMetadata = metaint;
139 }
140 catch (NumberFormatException nfe) {
141 }
142 }
143 }
144
145 /**
146 * IcyInputStream constructor for know meta-interval (Icecast 2)
147 * @param in
148 * @param metaint
149 * @throws IOException
150 */
151 public IcyInputStream(InputStream in, String metaIntString) throws IOException {
152 super(in);
153 tags = new HashMap();
154 tagParseSupport = new MP3TagParseSupport();
155 try
156 {
157 metaint = Integer.parseInt(metaIntString.trim());
158 if (DEBUG) System.out.println("METAINT:"+metaint);
159 bytesUntilNextMetadata = metaint;
160 }
161 catch (NumberFormatException nfe) {
162 }
163 }
164
165 /** Assuming we're at the top of the stream, read lines one
166 by one until we hit a completely blank \r\n. Parse the
167 data as IcyTags.
168 */
169 protected void readInitialHeaders() throws IOException {
170 String line = null;
171 while (!((line = readCRLFLine()).equals(""))) {
172 int colonIndex = line.indexOf(':');
173 // does it have a ':' separator
174 if (colonIndex == -1)
175 continue;
176 IcyTag tag =
177 new IcyTag(
178 line.substring(0, colonIndex),
179 line.substring(colonIndex + 1));
180 //System.out.println(tag);
181 addTag(tag);
182 }
183 }
184 /** Read everything up to the next CRLF, return it as
185 a String.
186 */
187 protected String readCRLFLine() throws IOException {
188 int i = 0;
189 for (; i < crlfBuffer.length; i++) {
190 byte aByte = (byte) read();
191 if (aByte == '\r') {
192 // possible end of line
193 byte anotherByte = (byte) read();
194 i++; // since we read again
195 if (anotherByte == '\n') {
196 break; // break out of while
197 }
198 else {
199 // oops, not end of line - put these in array
200 crlfBuffer[i - 1] = aByte;
201 crlfBuffer[i] = anotherByte;
202 }
203 }
204 else {
205 // if not \r
206 crlfBuffer[i] = aByte;
207 }
208 } // for
209 // get the string from the byte[]. i is 1 too high because of
210 // read-ahead in crlf block
211 return new String(crlfBuffer, 0, i - 1);
212 }
213 /** Reads and returns a single byte.
214 If the next byte is a metadata block, then that
215 block is read, stripped, and parsed before reading
216 and returning the first byte after the metadata block.
217 */
218 public int read() throws IOException {
219 if (bytesUntilNextMetadata > 0) {
220 bytesUntilNextMetadata--;
221 return super.read();
222 }
223 else if (bytesUntilNextMetadata == 0) {
224 // we need to read next metadata block
225 readMetadata();
226 bytesUntilNextMetadata = metaint - 1;
227 // -1 because we read byte on next line
228 return super.read();
229 }
230 else {
231 // no metadata in this stream
232 return super.read();
233 }
234 }
235 /** Reads a block of bytes. If the next byte is known
236 to be a block of metadata, then that is read, parsed,
237 and stripped, and then a block of bytes is read and
238 returned.
239 Otherwise, it may read up to but
240 not into the next metadata block if
241 <code>bytesUntilNextMetadata &lt; length</code>
242 */
243 public int read(byte[] buf, int offset, int length) throws IOException {
244 // if not on metadata, do the usual read so long as we
245 // don't read past metadata
246 if (bytesUntilNextMetadata > 0) {
247 int adjLength = Math.min(length, bytesUntilNextMetadata);
248 int got = super.read(buf, offset, adjLength);
249 bytesUntilNextMetadata -= got;
250 return got;
251 }
252 else if (bytesUntilNextMetadata == 0) {
253 // read/parse the metadata
254 readMetadata();
255 // now as above, except that we reset
256 // bytesUntilNextMetadata differently
257
258 //int adjLength = Math.min(length, bytesUntilNextMetadata);
259 //int got = super.read(buf, offset, adjLength);
260 //bytesUntilNextMetadata = metaint - got;
261
262 // Chop Fix - JavaZOOM (3 lines above seem buggy)
263 bytesUntilNextMetadata = metaint;
264 int adjLength = Math.min(length, bytesUntilNextMetadata);
265 int got = super.read(buf, offset, adjLength);
266 bytesUntilNextMetadata -= got;
267 // End fix - JavaZOOM
268
269
270 return got;
271 }
272 else {
273 // not even reading metadata
274 return super.read(buf, offset, length);
275 }
276 }
277 /** trivial <code>return read (buf, 0, buf.length)</code>
278 */
279 public int read(byte[] buf) throws IOException {
280 return read(buf, 0, buf.length);
281 }
282 /** Read the next segment of metadata. The stream <b>must</b>
283 be right on the segment, ie, the next byte to read is
284 the metadata block count. The metadata is parsed and
285 new tags are added with addTag(), which fires events
286 */
287 protected void readMetadata() throws IOException {
288 int blockCount = super.read();
289 if (DEBUG) System.out.println("BLOCKCOUNT:"+blockCount);
290 // System.out.println ("blocks to read: " + blockCount);
291 int byteCount = (blockCount * 16); // 16 bytes per block
292 if (byteCount < 0)
293 return; // WTF?!
294 byte[] metadataBlock = new byte[byteCount];
295 int index = 0;
296 // build an array of this metadata
297 while (byteCount > 0) {
298 int bytesRead = super.read(metadataBlock, index, byteCount);
299 index += bytesRead;
300 byteCount -= bytesRead;
301 }
302 // now parse it
303 if (blockCount > 0)
304 parseInlineIcyTags(metadataBlock);
305 } // readMetadata
306 /** Parse metadata from an in-stream "block" of bytes, add
307 a tag for each one.
308 <p>
309 Hilariously, the inline data format is totally different
310 than the top-of-stream header. For example, here's a
311 block I saw on "Final Fantasy Radio":
312 <pre>
313 StreamTitle='Final Fantasy 8 - Nobuo Uematsu - Blue Fields';StreamUrl='';
314 </pre>
315 In other words:
316 <ol>
317 <li>Tags are delimited by semicolons
318 <li>Keys/values are delimited by equals-signs
319 <li>Values are wrapped in single-quotes
320 <li>Key names are in SentenceCase, not lowercase-dashed
321 </ol>
322 */
323 protected void parseInlineIcyTags(byte[] tagBlock) {
324 String blockString = new String(tagBlock);
325 if (DEBUG) System.out.println("BLOCKSTR:"+blockString);
326 StringTokenizer izer =
327 new StringTokenizer(blockString, INLINE_TAG_SEPARATORS);
328 int i = 0;
329 while (izer.hasMoreTokens()) {
330 String tagString = izer.nextToken();
331 int separatorIdx = tagString.indexOf('=');
332 if (separatorIdx == -1)
333 continue; // bogus tagString if no '='
334 // try to strip single-quotes around value, if present
335 int valueStartIdx =
336 (tagString.charAt(separatorIdx + 1) == '\'')
337 ? separatorIdx + 2
338 : separatorIdx + 1;
339 int valueEndIdx =
340 (tagString.charAt(tagString.length() - 1)) == '\''
341 ? tagString.length() - 1
342 : tagString.length();
343 String name = tagString.substring(0, separatorIdx);
344 String value = tagString.substring(valueStartIdx, valueEndIdx);
345 // System.out.println (i++ + " " + name + ":" + value);
346 IcyTag tag = new IcyTag(name, value);
347 addTag(tag);
348 }
349 }
350 /** adds the tag to the HashMap of tags we have encountered
351 either in-stream or as headers, replacing any previous
352 tag with this name.
353 */
354 protected void addTag(IcyTag tag) {
355 tags.put(tag.getName(), tag);
356 // fire this as an event too
357 tagParseSupport.fireTagParsed(this, tag);
358 }
359 /** Get the named tag from the HashMap of headers and
360 in-line tags. Null if no such tag has been encountered.
361 */
362 public MP3Tag getTag(String tagName) {
363 return (MP3Tag) tags.get(tagName);
364 }
365 /** Get all tags (headers or in-stream) encountered thus far.
366 */
367 public MP3Tag[] getTags() {
368 return (MP3Tag[]) tags.values().toArray(new MP3Tag[0]);
369 }
370 /** Returns a HashMap of all headers and in-stream tags
371 parsed so far.
372 */
373 public HashMap getTagHash() {
374 return tags;
375 }
376 /** Adds a TagParseListener to be notified when this stream
377 parses MP3Tags.
378 */
379 public void addTagParseListener(TagParseListener tpl) {
380 tagParseSupport.addTagParseListener(tpl);
381 }
382 /** Removes a TagParseListener, so it won't be notified when
383 this stream parses MP3Tags.
384 */
385 public void removeTagParseListener(TagParseListener tpl) {
386 tagParseSupport.removeTagParseListener(tpl);
387 }
388 /** Quickie unit-test.
389 */
390 public static void main(String args[]) {
391 byte[] chow = new byte[200];
392 if (args.length != 1) {
393 //System.out.println("Usage: IcyInputStream <url>");
394 return;
395 }
396 try {
397 URL url = new URL(args[0]);
398 URLConnection conn = url.openConnection();
399 conn.setRequestProperty("Icy-Metadata", "1");
400 IcyInputStream icy =
401 new IcyInputStream(
402 new BufferedInputStream(conn.getInputStream()));
403 while (icy.available() > -1) {
404 // icy.read();
405 icy.read(chow, 0, chow.length);
406 }
407 }
408 catch (Exception e) {
409 e.printStackTrace();
410 }
411 }
412}
diff --git a/songdbj/javazoom/spi/mpeg/sampled/file/tag/IcyTag.java b/songdbj/javazoom/spi/mpeg/sampled/file/tag/IcyTag.java
deleted file mode 100644
index bbe70f1f3c..0000000000
--- a/songdbj/javazoom/spi/mpeg/sampled/file/tag/IcyTag.java
+++ /dev/null
@@ -1,42 +0,0 @@
1/*
2 * IcyTag.
3 *
4 * jicyshout : http://sourceforge.net/projects/jicyshout/
5 *
6 * JavaZOOM : mp3spi@javazoom.net
7 * http://www.javazoom.net
8 *
9 *-----------------------------------------------------------------------
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU Library General Public License as published
12 * by the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Library General Public License for more details.
19 *
20 * You should have received a copy of the GNU Library General Public
21 * License along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 *----------------------------------------------------------------------
24 */
25
26package javazoom.spi.mpeg.sampled.file.tag;
27/**
28 * A tag parsed from an icecast tag.
29 */
30public class IcyTag extends MP3Tag implements StringableTag {
31 /** Create a new tag, from the parsed name and (String) value.
32 It looks like all Icecast tags are Strings (safe to assume
33 this going forward?)
34 */
35 public IcyTag(String name, String stringValue) {
36 super(name, stringValue);
37 }
38 // so far as I know, all Icecast tags are strings
39 public String getValueAsString() {
40 return (String) getValue();
41 }
42}
diff --git a/songdbj/javazoom/spi/mpeg/sampled/file/tag/MP3MetadataParser.java b/songdbj/javazoom/spi/mpeg/sampled/file/tag/MP3MetadataParser.java
deleted file mode 100644
index 81511064b8..0000000000
--- a/songdbj/javazoom/spi/mpeg/sampled/file/tag/MP3MetadataParser.java
+++ /dev/null
@@ -1,50 +0,0 @@
1/*
2 * MP3MetadataParser.
3 *
4 * jicyshout : http://sourceforge.net/projects/jicyshout/
5 *
6 * JavaZOOM : mp3spi@javazoom.net
7 * http://www.javazoom.net
8 *
9 *-----------------------------------------------------------------------
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU Library General Public License as published
12 * by the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Library General Public License for more details.
19 *
20 * You should have received a copy of the GNU Library General Public
21 * License along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 *----------------------------------------------------------------------
24 */
25
26package javazoom.spi.mpeg.sampled.file.tag;
27
28/** An object that fires off TagParseEvents as they are parsed
29 from a stream, ServerSocket, or other metadata source
30 */
31public interface MP3MetadataParser {
32 /** Adds a TagParseListener to be notified when this object
33 parses MP3Tags.
34 */
35 public void addTagParseListener(TagParseListener tpl);
36 /** Removes a TagParseListener, so it won't be notified when
37 this object parses MP3Tags.
38 */
39 public void removeTagParseListener(TagParseListener tpl);
40 /** Get all tags (headers or in-stream) encountered thusfar.
41 This is included in this otherwise Listener-like scheme
42 because most standards are a mix of start-of-stream
43 metadata tags (like the http headers or the stuff at the
44 top of an ice stream) and inline data. Implementations should
45 hang onto all tags they parse and provide them with this
46 call. Callers should first use this call to get initial
47 tags, then subscribe for events as the stream continues.
48 */
49 public MP3Tag[] getTags();
50}
diff --git a/songdbj/javazoom/spi/mpeg/sampled/file/tag/MP3Tag.java b/songdbj/javazoom/spi/mpeg/sampled/file/tag/MP3Tag.java
deleted file mode 100644
index b545356240..0000000000
--- a/songdbj/javazoom/spi/mpeg/sampled/file/tag/MP3Tag.java
+++ /dev/null
@@ -1,52 +0,0 @@
1/*
2 * MP3Tag.
3 *
4 * jicyshout : http://sourceforge.net/projects/jicyshout/
5 *
6 * JavaZOOM : mp3spi@javazoom.net
7 * http://www.javazoom.net
8 *
9 *-----------------------------------------------------------------------
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU Library General Public License as published
12 * by the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Library General Public License for more details.
19 *
20 * You should have received a copy of the GNU Library General Public
21 * License along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 *----------------------------------------------------------------------
24 */
25
26package javazoom.spi.mpeg.sampled.file.tag;
27
28/** An individual piece of mp3 metadata, as a name/value pair.
29 Abstract just so that subclasses will indicate their
30 tagging scheme (Icy, ID3, etc.).
31 */
32public abstract class MP3Tag extends Object {
33 protected String name;
34 protected Object value;
35 public MP3Tag(String name, Object value) {
36 this.name = name;
37 this.value = value;
38 }
39 public String getName() {
40 return name;
41 }
42 public Object getValue() {
43 return value;
44 }
45 public String toString() {
46 return getClass().getName()
47 + " -- "
48 + getName()
49 + ":"
50 + getValue().toString();
51 }
52}
diff --git a/songdbj/javazoom/spi/mpeg/sampled/file/tag/MP3TagParseSupport.java b/songdbj/javazoom/spi/mpeg/sampled/file/tag/MP3TagParseSupport.java
deleted file mode 100644
index 1ab6525512..0000000000
--- a/songdbj/javazoom/spi/mpeg/sampled/file/tag/MP3TagParseSupport.java
+++ /dev/null
@@ -1,62 +0,0 @@
1/*
2 * MP3TagParseSupport.
3 *
4 * jicyshout : http://sourceforge.net/projects/jicyshout/
5 *
6 * JavaZOOM : mp3spi@javazoom.net
7 * http://www.javazoom.net
8 *
9 *-----------------------------------------------------------------------
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU Library General Public License as published
12 * by the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Library General Public License for more details.
19 *
20 * You should have received a copy of the GNU Library General Public
21 * License along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 *----------------------------------------------------------------------
24 */
25
26package javazoom.spi.mpeg.sampled.file.tag;
27
28import java.util.ArrayList;
29/**
30*/
31public class MP3TagParseSupport extends Object {
32 ArrayList tagParseListeners;
33 /** trivial constructor, sets up listeners list.
34 */
35 public MP3TagParseSupport() {
36 super();
37 tagParseListeners = new ArrayList();
38 }
39 /** Adds a TagParseListener to be notified when a stream
40 parses MP3Tags.
41 */
42 public void addTagParseListener(TagParseListener tpl) {
43 tagParseListeners.add(tpl);
44 }
45 /** Removes a TagParseListener, so it won't be notified when
46 a stream parses MP3Tags.
47 */
48 public void removeTagParseListener(TagParseListener tpl) {
49 tagParseListeners.add(tpl);
50 }
51 /** Fires the given event to all registered listeners
52 */
53 public void fireTagParseEvent(TagParseEvent tpe) {
54 for (int i = 0; i < tagParseListeners.size(); i++) {
55 TagParseListener l = (TagParseListener) tagParseListeners.get(i);
56 l.tagParsed(tpe);
57 }
58 }
59 public void fireTagParsed(Object source, MP3Tag tag) {
60 fireTagParseEvent(new TagParseEvent(source, tag));
61 }
62}
diff --git a/songdbj/javazoom/spi/mpeg/sampled/file/tag/StringableTag.java b/songdbj/javazoom/spi/mpeg/sampled/file/tag/StringableTag.java
deleted file mode 100644
index 685c5207f9..0000000000
--- a/songdbj/javazoom/spi/mpeg/sampled/file/tag/StringableTag.java
+++ /dev/null
@@ -1,36 +0,0 @@
1/*
2 * StringableTag.
3 *
4 * jicyshout : http://sourceforge.net/projects/jicyshout/
5 *
6 * JavaZOOM : mp3spi@javazoom.net
7 * http://www.javazoom.net
8 *
9 *-----------------------------------------------------------------------
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU Library General Public License as published
12 * by the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Library General Public License for more details.
19 *
20 * You should have received a copy of the GNU Library General Public
21 * License along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 *----------------------------------------------------------------------
24 */
25
26package javazoom.spi.mpeg.sampled.file.tag;
27
28/** Indicates that the value of a tag is a string, and
29 provides a getValueAsString() method to get it.
30 Appropriate for tags like artist, title, etc.
31 */
32public interface StringableTag {
33 /** Return the value of this tag as a string.
34 */
35 public String getValueAsString();
36}
diff --git a/songdbj/javazoom/spi/mpeg/sampled/file/tag/TagParseEvent.java b/songdbj/javazoom/spi/mpeg/sampled/file/tag/TagParseEvent.java
deleted file mode 100644
index 97e9ec1e19..0000000000
--- a/songdbj/javazoom/spi/mpeg/sampled/file/tag/TagParseEvent.java
+++ /dev/null
@@ -1,44 +0,0 @@
1/*
2 * TagParseEvent.
3 *
4 * jicyshout : http://sourceforge.net/projects/jicyshout/
5 *
6 * JavaZOOM : mp3spi@javazoom.net
7 * http://www.javazoom.net
8 *
9 *-----------------------------------------------------------------------
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU Library General Public License as published
12 * by the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Library General Public License for more details.
19 *
20 * You should have received a copy of the GNU Library General Public
21 * License along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 *----------------------------------------------------------------------
24 */
25
26package javazoom.spi.mpeg.sampled.file.tag;
27
28import java.util.EventObject;
29/** Event to indicate that an MP3 tag was received through
30 some means (parsed in stream, received via UDP, whatever)
31 and converted into an MP3Tag object.
32 */
33public class TagParseEvent extends EventObject {
34 protected MP3Tag tag;
35 public TagParseEvent(Object source, MP3Tag tag) {
36 super(source);
37 this.tag = tag;
38 }
39 /** Get the tag that was parsed.
40 */
41 public MP3Tag getTag() {
42 return tag;
43 }
44}
diff --git a/songdbj/javazoom/spi/mpeg/sampled/file/tag/TagParseListener.java b/songdbj/javazoom/spi/mpeg/sampled/file/tag/TagParseListener.java
deleted file mode 100644
index a630827297..0000000000
--- a/songdbj/javazoom/spi/mpeg/sampled/file/tag/TagParseListener.java
+++ /dev/null
@@ -1,37 +0,0 @@
1/*
2 * TagParseListener.
3 *
4 * jicyshout : http://sourceforge.net/projects/jicyshout/
5 *
6 * JavaZOOM : mp3spi@javazoom.net
7 * http://www.javazoom.net
8 *
9 *-----------------------------------------------------------------------
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU Library General Public License as published
12 * by the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Library General Public License for more details.
19 *
20 * You should have received a copy of the GNU Library General Public
21 * License along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 *----------------------------------------------------------------------
24 */
25
26package javazoom.spi.mpeg.sampled.file.tag;
27
28import java.util.EventListener;
29/** EventListener to be implemented by objects that want to
30 get callbacks when MP3 tags are received.
31 */
32public interface TagParseListener extends EventListener {
33 /** Called when a tag is found (parsed from the stream,
34 received via UDP, etc.)
35 */
36 public void tagParsed(TagParseEvent tpe);
37}