diff options
Diffstat (limited to 'songdbj/javazoom/spi/mpeg/sampled/convert')
-rw-r--r-- | songdbj/javazoom/spi/mpeg/sampled/convert/DecodedMpegAudioInputStream.java | 334 | ||||
-rw-r--r-- | songdbj/javazoom/spi/mpeg/sampled/convert/MpegFormatConversionProvider.java | 120 |
2 files changed, 454 insertions, 0 deletions
diff --git a/songdbj/javazoom/spi/mpeg/sampled/convert/DecodedMpegAudioInputStream.java b/songdbj/javazoom/spi/mpeg/sampled/convert/DecodedMpegAudioInputStream.java new file mode 100644 index 0000000000..e4e6fed00a --- /dev/null +++ b/songdbj/javazoom/spi/mpeg/sampled/convert/DecodedMpegAudioInputStream.java | |||
@@ -0,0 +1,334 @@ | |||
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 | |||
24 | package javazoom.spi.mpeg.sampled.convert; | ||
25 | import java.io.IOException; | ||
26 | import java.io.InputStream; | ||
27 | import java.util.HashMap; | ||
28 | import java.util.Map; | ||
29 | |||
30 | import javax.sound.sampled.AudioFormat; | ||
31 | import javax.sound.sampled.AudioInputStream; | ||
32 | |||
33 | import javazoom.jl.decoder.Bitstream; | ||
34 | import javazoom.jl.decoder.BitstreamException; | ||
35 | import javazoom.jl.decoder.Decoder; | ||
36 | import javazoom.jl.decoder.DecoderException; | ||
37 | import javazoom.jl.decoder.Equalizer; | ||
38 | import javazoom.jl.decoder.Header; | ||
39 | import javazoom.jl.decoder.Obuffer; | ||
40 | import javazoom.spi.PropertiesContainer; | ||
41 | import javazoom.spi.mpeg.sampled.file.IcyListener; | ||
42 | import javazoom.spi.mpeg.sampled.file.tag.TagParseEvent; | ||
43 | import javazoom.spi.mpeg.sampled.file.tag.TagParseListener; | ||
44 | |||
45 | import org.tritonus.share.TDebug; | ||
46 | import org.tritonus.share.sampled.convert.TAsynchronousFilteredAudioInputStream; | ||
47 | |||
48 | /** | ||
49 | * Main decoder. | ||
50 | */ | ||
51 | public 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 new file mode 100644 index 0000000000..1a3d51d9ad --- /dev/null +++ b/songdbj/javazoom/spi/mpeg/sampled/convert/MpegFormatConversionProvider.java | |||
@@ -0,0 +1,120 @@ | |||
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 | |||
25 | package javazoom.spi.mpeg.sampled.convert; | ||
26 | |||
27 | |||
28 | import java.util.Arrays; | ||
29 | |||
30 | import javax.sound.sampled.AudioFormat; | ||
31 | import javax.sound.sampled.AudioInputStream; | ||
32 | import javax.sound.sampled.AudioSystem; | ||
33 | |||
34 | import javazoom.spi.mpeg.sampled.file.MpegEncoding; | ||
35 | |||
36 | import org.tritonus.share.TDebug; | ||
37 | import org.tritonus.share.sampled.Encodings; | ||
38 | import org.tritonus.share.sampled.convert.TEncodingFormatConversionProvider; | ||
39 | |||
40 | /** | ||
41 | * ConversionProvider for MPEG files. | ||
42 | */ | ||
43 | public 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 | } | ||