summaryrefslogtreecommitdiff
path: root/songdbj/javazoom/jl/converter/Converter.java
diff options
context:
space:
mode:
Diffstat (limited to 'songdbj/javazoom/jl/converter/Converter.java')
-rw-r--r--songdbj/javazoom/jl/converter/Converter.java411
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
21package javazoom.jl.converter;
22
23import java.io.BufferedInputStream;
24import java.io.File;
25import java.io.FileInputStream;
26import java.io.IOException;
27import java.io.InputStream;
28import java.io.PrintWriter;
29
30import javazoom.jl.decoder.Bitstream;
31import javazoom.jl.decoder.Decoder;
32import javazoom.jl.decoder.Header;
33import javazoom.jl.decoder.JavaLayerException;
34import 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 */
47public 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