diff options
Diffstat (limited to 'songdbj/javazoom/jl/converter/Converter.java')
-rw-r--r-- | songdbj/javazoom/jl/converter/Converter.java | 411 |
1 files changed, 411 insertions, 0 deletions
diff --git a/songdbj/javazoom/jl/converter/Converter.java b/songdbj/javazoom/jl/converter/Converter.java new file mode 100644 index 0000000000..845082e626 --- /dev/null +++ b/songdbj/javazoom/jl/converter/Converter.java | |||
@@ -0,0 +1,411 @@ | |||
1 | /* | ||
2 | * 11/19/04 1.0 moved to LGPL. | ||
3 | * 12/12/99 Original verion. mdm@techie.com. | ||
4 | *----------------------------------------------------------------------- | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU Library General Public License as published | ||
7 | * by the Free Software Foundation; either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU Library General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU Library General Public | ||
16 | * License along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
18 | *---------------------------------------------------------------------- | ||
19 | */ | ||
20 | |||
21 | package javazoom.jl.converter; | ||
22 | |||
23 | import java.io.BufferedInputStream; | ||
24 | import java.io.File; | ||
25 | import java.io.FileInputStream; | ||
26 | import java.io.IOException; | ||
27 | import java.io.InputStream; | ||
28 | import java.io.PrintWriter; | ||
29 | |||
30 | import javazoom.jl.decoder.Bitstream; | ||
31 | import javazoom.jl.decoder.Decoder; | ||
32 | import javazoom.jl.decoder.Header; | ||
33 | import javazoom.jl.decoder.JavaLayerException; | ||
34 | import javazoom.jl.decoder.Obuffer; | ||
35 | |||
36 | /** | ||
37 | * The <code>Converter</code> class implements the conversion of | ||
38 | * an MPEG audio file to a .WAV file. To convert an MPEG audio stream, | ||
39 | * just create an instance of this class and call the convert() | ||
40 | * method, passing in the names of the input and output files. You can | ||
41 | * pass in optional <code>ProgressListener</code> and | ||
42 | * <code>Decoder.Params</code> objects also to customize the conversion. | ||
43 | * | ||
44 | * @author MDM 12/12/99 | ||
45 | * @since 0.0.7 | ||
46 | */ | ||
47 | public class Converter | ||
48 | { | ||
49 | /** | ||
50 | * Creates a new converter instance. | ||
51 | */ | ||
52 | public Converter() | ||
53 | { | ||
54 | } | ||
55 | |||
56 | public synchronized void convert(String sourceName, String destName) | ||
57 | throws JavaLayerException | ||
58 | { | ||
59 | convert(sourceName, destName, null, null); | ||
60 | } | ||
61 | |||
62 | public synchronized void convert(String sourceName, String destName, | ||
63 | ProgressListener progressListener) | ||
64 | throws JavaLayerException | ||
65 | { | ||
66 | convert(sourceName, destName, progressListener, null); | ||
67 | } | ||
68 | |||
69 | |||
70 | public void convert(String sourceName, String destName, | ||
71 | ProgressListener progressListener, Decoder.Params decoderParams) | ||
72 | throws JavaLayerException | ||
73 | { | ||
74 | if (destName.length()==0) | ||
75 | destName = null; | ||
76 | try { | ||
77 | InputStream in = openInput(sourceName); | ||
78 | convert(in, destName, progressListener, decoderParams); | ||
79 | in.close(); | ||
80 | } catch(IOException ioe) { | ||
81 | throw new JavaLayerException(ioe.getLocalizedMessage(), ioe); | ||
82 | } | ||
83 | } | ||
84 | |||
85 | public synchronized void convert(InputStream sourceStream, String destName, | ||
86 | ProgressListener progressListener, Decoder.Params decoderParams) | ||
87 | throws JavaLayerException | ||
88 | { | ||
89 | if (progressListener==null) | ||
90 | progressListener = PrintWriterProgressListener.newStdOut( | ||
91 | PrintWriterProgressListener.NO_DETAIL); | ||
92 | try { | ||
93 | if (!(sourceStream instanceof BufferedInputStream)) | ||
94 | sourceStream = new BufferedInputStream(sourceStream); | ||
95 | int frameCount = -1; | ||
96 | if (sourceStream.markSupported()) { | ||
97 | sourceStream.mark(-1); | ||
98 | frameCount = countFrames(sourceStream); | ||
99 | sourceStream.reset(); | ||
100 | } | ||
101 | progressListener.converterUpdate(ProgressListener.UPDATE_FRAME_COUNT, frameCount, 0); | ||
102 | |||
103 | |||
104 | Obuffer output = null; | ||
105 | Decoder decoder = new Decoder(decoderParams); | ||
106 | Bitstream stream = new Bitstream(sourceStream); | ||
107 | |||
108 | if (frameCount==-1) | ||
109 | frameCount = Integer.MAX_VALUE; | ||
110 | |||
111 | int frame = 0; | ||
112 | long startTime = System.currentTimeMillis(); | ||
113 | |||
114 | try | ||
115 | { | ||
116 | for (; frame<frameCount; frame++) | ||
117 | { | ||
118 | try | ||
119 | { | ||
120 | Header header = stream.readFrame(); | ||
121 | if (header==null) | ||
122 | break; | ||
123 | |||
124 | progressListener.readFrame(frame, header); | ||
125 | |||
126 | if (output==null) | ||
127 | { | ||
128 | // REVIEW: Incorrect functionality. | ||
129 | // the decoder should provide decoded | ||
130 | // frequency and channels output as it may differ from | ||
131 | // the source (e.g. when downmixing stereo to mono.) | ||
132 | int channels = (header.mode()==Header.SINGLE_CHANNEL) ? 1 : 2; | ||
133 | int freq = header.frequency(); | ||
134 | output = new WaveFileObuffer(channels, freq, destName); | ||
135 | decoder.setOutputBuffer(output); | ||
136 | } | ||
137 | |||
138 | Obuffer decoderOutput = decoder.decodeFrame(header, stream); | ||
139 | |||
140 | // REVIEW: the way the output buffer is set | ||
141 | // on the decoder is a bit dodgy. Even though | ||
142 | // this exception should never happen, we test to be sure. | ||
143 | if (decoderOutput!=output) | ||
144 | throw new InternalError("Output buffers are different."); | ||
145 | |||
146 | |||
147 | progressListener.decodedFrame(frame, header, output); | ||
148 | |||
149 | stream.closeFrame(); | ||
150 | |||
151 | } | ||
152 | catch (Exception ex) | ||
153 | { | ||
154 | boolean stop = !progressListener.converterException(ex); | ||
155 | |||
156 | if (stop) | ||
157 | { | ||
158 | throw new JavaLayerException(ex.getLocalizedMessage(), ex); | ||
159 | } | ||
160 | } | ||
161 | } | ||
162 | |||
163 | } | ||
164 | finally | ||
165 | { | ||
166 | |||
167 | if (output!=null) | ||
168 | output.close(); | ||
169 | } | ||
170 | |||
171 | int time = (int)(System.currentTimeMillis()-startTime); | ||
172 | progressListener.converterUpdate(ProgressListener.UPDATE_CONVERT_COMPLETE, | ||
173 | time, frame); | ||
174 | } | ||
175 | catch (IOException ex) | ||
176 | { | ||
177 | throw new JavaLayerException(ex.getLocalizedMessage(), ex); | ||
178 | } | ||
179 | } | ||
180 | |||
181 | |||
182 | protected int countFrames(InputStream in) | ||
183 | { | ||
184 | return -1; | ||
185 | } | ||
186 | |||
187 | |||
188 | protected InputStream openInput(String fileName) | ||
189 | throws IOException | ||
190 | { | ||
191 | // ensure name is abstract path name | ||
192 | File file = new File(fileName); | ||
193 | InputStream fileIn = new FileInputStream(file); | ||
194 | BufferedInputStream bufIn = new BufferedInputStream(fileIn); | ||
195 | |||
196 | return bufIn; | ||
197 | } | ||
198 | |||
199 | |||
200 | /** | ||
201 | * This interface is used by the Converter to provide | ||
202 | * notification of tasks being carried out by the converter, | ||
203 | * and to provide new information as it becomes available. | ||
204 | */ | ||
205 | |||
206 | static public interface ProgressListener | ||
207 | { | ||
208 | public static final int UPDATE_FRAME_COUNT = 1; | ||
209 | |||
210 | /** | ||
211 | * Conversion is complete. Param1 contains the time | ||
212 | * to convert in milliseconds. Param2 contains the number | ||
213 | * of MPEG audio frames converted. | ||
214 | */ | ||
215 | public static final int UPDATE_CONVERT_COMPLETE = 2; | ||
216 | |||
217 | |||
218 | /** | ||
219 | * Notifies the listener that new information is available. | ||
220 | * | ||
221 | * @param updateID Code indicating the information that has been | ||
222 | * updated. | ||
223 | * | ||
224 | * @param param1 Parameter whose value depends upon the update code. | ||
225 | * @param param2 Parameter whose value depends upon the update code. | ||
226 | * | ||
227 | * The <code>updateID</code> parameter can take these values: | ||
228 | * | ||
229 | * UPDATE_FRAME_COUNT: param1 is the frame count, or -1 if not known. | ||
230 | * UPDATE_CONVERT_COMPLETE: param1 is the conversion time, param2 | ||
231 | * is the number of frames converted. | ||
232 | */ | ||
233 | public void converterUpdate(int updateID, int param1, int param2); | ||
234 | |||
235 | /** | ||
236 | * If the converter wishes to make a first pass over the | ||
237 | * audio frames, this is called as each frame is parsed. | ||
238 | */ | ||
239 | public void parsedFrame(int frameNo, Header header); | ||
240 | |||
241 | /** | ||
242 | * This method is called after each frame has been read, | ||
243 | * but before it has been decoded. | ||
244 | * | ||
245 | * @param frameNo The 0-based sequence number of the frame. | ||
246 | * @param header The Header rerpesenting the frame just read. | ||
247 | */ | ||
248 | public void readFrame(int frameNo, Header header); | ||
249 | |||
250 | /** | ||
251 | * This method is called after a frame has been decoded. | ||
252 | * | ||
253 | * @param frameNo The 0-based sequence number of the frame. | ||
254 | * @param header The Header rerpesenting the frame just read. | ||
255 | * @param o The Obuffer the deocded data was written to. | ||
256 | */ | ||
257 | public void decodedFrame(int frameNo, Header header, Obuffer o); | ||
258 | |||
259 | /** | ||
260 | * Called when an exception is thrown during while converting | ||
261 | * a frame. | ||
262 | * | ||
263 | * @param t The <code>Throwable</code> instance that | ||
264 | * was thrown. | ||
265 | * | ||
266 | * @return <code>true</code> to continue processing, or false | ||
267 | * to abort conversion. | ||
268 | * | ||
269 | * If this method returns <code>false</code>, the exception | ||
270 | * is propagated to the caller of the convert() method. If | ||
271 | * <code>true</code> is returned, the exception is silently | ||
272 | * ignored and the converter moves onto the next frame. | ||
273 | */ | ||
274 | public boolean converterException(Throwable t); | ||
275 | |||
276 | } | ||
277 | |||
278 | |||
279 | /** | ||
280 | * Implementation of <code>ProgressListener</code> that writes | ||
281 | * notification text to a <code>PrintWriter</code>. | ||
282 | */ | ||
283 | // REVIEW: i18n of text and order required. | ||
284 | static public class PrintWriterProgressListener implements ProgressListener | ||
285 | { | ||
286 | static public final int NO_DETAIL = 0; | ||
287 | |||
288 | /** | ||
289 | * Level of detail typically expected of expert | ||
290 | * users. | ||
291 | */ | ||
292 | static public final int EXPERT_DETAIL = 1; | ||
293 | |||
294 | /** | ||
295 | * Verbose detail. | ||
296 | */ | ||
297 | static public final int VERBOSE_DETAIL = 2; | ||
298 | |||
299 | /** | ||
300 | * Debug detail. All frame read notifications are shown. | ||
301 | */ | ||
302 | static public final int DEBUG_DETAIL = 7; | ||
303 | |||
304 | static public final int MAX_DETAIL = 10; | ||
305 | |||
306 | private PrintWriter pw; | ||
307 | |||
308 | private int detailLevel; | ||
309 | |||
310 | static public PrintWriterProgressListener newStdOut(int detail) | ||
311 | { | ||
312 | return new PrintWriterProgressListener( | ||
313 | new PrintWriter(System.out, true), detail); | ||
314 | } | ||
315 | |||
316 | public PrintWriterProgressListener(PrintWriter writer, int detailLevel) | ||
317 | { | ||
318 | this.pw = writer; | ||
319 | this.detailLevel = detailLevel; | ||
320 | } | ||
321 | |||
322 | |||
323 | public boolean isDetail(int detail) | ||
324 | { | ||
325 | return (this.detailLevel >= detail); | ||
326 | } | ||
327 | |||
328 | public void converterUpdate(int updateID, int param1, int param2) | ||
329 | { | ||
330 | if (isDetail(VERBOSE_DETAIL)) | ||
331 | { | ||
332 | switch (updateID) | ||
333 | { | ||
334 | case UPDATE_CONVERT_COMPLETE: | ||
335 | // catch divide by zero errors. | ||
336 | if (param2==0) | ||
337 | param2 = 1; | ||
338 | |||
339 | pw.println(); | ||
340 | pw.println("Converted "+param2+" frames in "+param1+" ms ("+ | ||
341 | (param1/param2)+" ms per frame.)"); | ||
342 | } | ||
343 | } | ||
344 | } | ||
345 | |||
346 | public void parsedFrame(int frameNo, Header header) | ||
347 | { | ||
348 | if ((frameNo==0) && isDetail(VERBOSE_DETAIL)) | ||
349 | { | ||
350 | String headerString = header.toString(); | ||
351 | pw.println("File is a "+headerString); | ||
352 | } | ||
353 | else if (isDetail(MAX_DETAIL)) | ||
354 | { | ||
355 | String headerString = header.toString(); | ||
356 | pw.println("Prased frame "+frameNo+": "+headerString); | ||
357 | } | ||
358 | } | ||
359 | |||
360 | public void readFrame(int frameNo, Header header) | ||
361 | { | ||
362 | if ((frameNo==0) && isDetail(VERBOSE_DETAIL)) | ||
363 | { | ||
364 | String headerString = header.toString(); | ||
365 | pw.println("File is a "+headerString); | ||
366 | } | ||
367 | else if (isDetail(MAX_DETAIL)) | ||
368 | { | ||
369 | String headerString = header.toString(); | ||
370 | pw.println("Read frame "+frameNo+": "+headerString); | ||
371 | } | ||
372 | } | ||
373 | |||
374 | public void decodedFrame(int frameNo, Header header, Obuffer o) | ||
375 | { | ||
376 | if (isDetail(MAX_DETAIL)) | ||
377 | { | ||
378 | String headerString = header.toString(); | ||
379 | pw.println("Decoded frame "+frameNo+": "+headerString); | ||
380 | pw.println("Output: "+o); | ||
381 | } | ||
382 | else if (isDetail(VERBOSE_DETAIL)) | ||
383 | { | ||
384 | if (frameNo==0) | ||
385 | { | ||
386 | pw.print("Converting."); | ||
387 | pw.flush(); | ||
388 | } | ||
389 | |||
390 | if ((frameNo % 10)==0) | ||
391 | { | ||
392 | pw.print('.'); | ||
393 | pw.flush(); | ||
394 | } | ||
395 | } | ||
396 | } | ||
397 | |||
398 | public boolean converterException(Throwable t) | ||
399 | { | ||
400 | if (this.detailLevel>NO_DETAIL) | ||
401 | { | ||
402 | t.printStackTrace(pw); | ||
403 | pw.flush(); | ||
404 | } | ||
405 | return false; | ||
406 | } | ||
407 | |||
408 | } | ||
409 | |||
410 | |||
411 | } \ No newline at end of file | ||