Source code: com/tuneology/avm/AudioConverter.java
1 /*
2 AudioConverter.java
3
4 Copyright (C) 2002 Fran Taylor
5
6 This file is part of java-avm.
7
8 java-avm is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as
10 published by the Free Software Foundation; either version 2 of the
11 License, or (at your option) any later version.
12
13 java-avm is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public
19 License along with java-avm; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 USA
22
23 $Id: AudioConverter.java,v 1.5 2002/11/05 08:06:44 xnarf Exp $
24
25 */
26
27 package com.tuneology.avm;
28
29 import java.io.*;
30 import java.util.*;
31 import java.awt.*;
32 import javax.swing.*;
33
34 /**
35 * Utilities for converting audio files to different types.
36 *
37 * @version $Id: AudioConverter.java,v 1.5 2002/11/05 08:06:44 xnarf Exp $
38 *
39 * @author Fran Taylor
40 */
41
42 public class AudioConverter {
43 private AudioConverter() { }
44 /**
45 * Returns a string containing the versions of all the format conversion programs.
46 *
47 * @return a string containing the versions of all the format conversion programs.
48 */
49 public static String getVersions() {
50 return notlameVersion() + "\n" +
51 (useMpg123 ? mpg123Version() : madplayVersion()) + "\n" +
52 oggencVersion() + "\n" + ogg123Version() + "\n";
53 }
54 /**
55 * Given a file, creates a new object representing a local version of the file,
56 * and copies the file from the remote store to the local file system.
57 *
58 * @param dir The directory in which to write the file.
59 * @param f The souce file.
60 * @param pm The GUI status indicator.
61 * @param isTemp If true, marks the file as tempoary (to be deleted on exit)
62 * @throws IOException
63 */
64 public static File makeLocalCopy(File dir, File f, ProgressListener pm, boolean isTemp) throws IOException {
65 // construct a the new filename
66 String basePath = dir.getCanonicalPath();
67 String suf = "";
68 String fname = f.getName();
69 int pos = fname.lastIndexOf(".");
70 if (pos != -1) {
71 suf = fname.substring(pos + 1);
72 basePath += fname.substring(0, pos - 1);
73 } else {
74 basePath += fname;
75 }
76 int ind = -1;
77 // if it exists, try appending _1, etc. to the
78 // file name until one is found
79 File nf;
80 while ((nf = new File(fname)).exists()) {
81 if (ind == -1) ind = 1; else ind++;
82 fname = basePath + "_" + ind + suf;
83 }
84 // okay, now we actually make the copy
85 FileInputStream istr = null;
86 FileOutputStream ostr = null;
87 try {
88 istr = new FileInputStream(f);
89 ostr = new FileOutputStream(nf);
90 byte buf[] = new byte[COPY_BUFSIZ];
91 int n;
92 while((n = istr.read(buf, 0, buf.length)) > 0) {
93 ostr.write(buf, 0, n);
94 }
95 } finally {
96 if (istr != null) istr.close();
97 if (ostr != null) ostr.close();
98 }
99 return nf;
100 }
101 /**
102 * Returns the file type.
103 *
104 * @param f the file in question.
105 * @return the file type, or -1 if not known.
106 */
107 public static int getFileFormat(File f) {
108 try {
109 String path = f.getCanonicalPath();
110 if (path.endsWith(".mp3")) {
111 return FormatPrefs.MP3_FORMAT;
112 } else if (path.endsWith(".wav")) {
113 return FormatPrefs.WAV_FORMAT;
114 } else if (path.endsWith(".ogg")) {
115 return FormatPrefs.OGG_FORMAT;
116 } else if (path.endsWith(".wma")) {
117 return FormatPrefs.WMA_FORMAT;
118 }
119 } catch (IOException ex) { }
120 return -1;
121
122 }
123 /**
124 * returns the number of bytes of space required
125 * to store the specified file according to the
126 * specified preferences.
127 *
128 * @param finfo
129 * @param prefs
130 * @return the number of bytes required to store the data
131 * @throws IOException if the format parameters are invalid
132 */
133 public static long spaceRequired(TocEntry finfo, FormatPrefs prefs) throws Exception {
134 if (finfo.len == 0) return finfo.fileSize;
135 long rate;
136 switch(prefs.format) {
137 case FormatPrefs.MP3_FORMAT:
138 switch(prefs.quality) {
139 case FormatPrefs.LOW_QUALITY: rate = MP3_LOW_QUALITY_RATE; break;
140 case FormatPrefs.MED_QUALITY:
141 case FormatPrefs.DEFAULT_QUALITY: rate = MP3_MED_QUALITY_RATE; break;
142 case FormatPrefs.HIGH_QUALITY: rate = MP3_HIGH_QUALITY_RATE; break;
143 case FormatPrefs.CUSTOM_QUALITY: rate = prefs.bitRate; break;
144 default: throw new IOException("invalid value for quality");
145 }
146 break;
147 case FormatPrefs.WAV_FORMAT:
148 switch(prefs.quality) {
149 case FormatPrefs.LOW_QUALITY: rate = WAV_LOW_QUALITY_RATE; break;
150 case FormatPrefs.MED_QUALITY:
151 case FormatPrefs.DEFAULT_QUALITY: rate = WAV_MED_QUALITY_RATE; break;
152 case FormatPrefs.HIGH_QUALITY: rate = WAV_HIGH_QUALITY_RATE; break;
153 case FormatPrefs.CUSTOM_QUALITY: rate = prefs.bitRate; break;
154 default: throw new IOException("invalid value for quality");
155 }
156 break;
157 case FormatPrefs.OGG_FORMAT:
158 switch(prefs.quality) {
159 case FormatPrefs.LOW_QUALITY: rate = OGG_LOW_QUALITY_RATE; break;
160 case FormatPrefs.MED_QUALITY:
161 case FormatPrefs.DEFAULT_QUALITY: rate = OGG_MED_QUALITY_RATE; break;
162 case FormatPrefs.HIGH_QUALITY: rate = OGG_HIGH_QUALITY_RATE; break;
163 case FormatPrefs.CUSTOM_QUALITY: rate = prefs.bitRate; break;
164 default: throw new IOException("invalid value for quality");
165 }
166 break;
167 case FormatPrefs.WMA_FORMAT:
168 switch(prefs.quality) {
169 case FormatPrefs.LOW_QUALITY: rate = WMA_LOW_QUALITY_RATE; break;
170 case FormatPrefs.MED_QUALITY:
171 case FormatPrefs.DEFAULT_QUALITY: rate = WMA_MED_QUALITY_RATE; break;
172 case FormatPrefs.HIGH_QUALITY: rate = WMA_HIGH_QUALITY_RATE; break;
173 case FormatPrefs.CUSTOM_QUALITY: rate = prefs.bitRate; break;
174 default: throw new IOException("invalid value for quality");
175 }
176 default: throw new IOException("invalid value for format");
177 }
178 // don't upsample, except for WAV files
179 if ((prefs.format != FormatPrefs.WAV_FORMAT) && (rate > finfo.rate)) rate = finfo.rate;
180 rate /= 16;
181 long n = (rate * finfo.len * ((prefs.mode == FormatPrefs.MONO_MODE) ? 1l : 2l)) / 75l;
182 return n;
183 }
184 /**
185 * Converts a file from one format to another
186 *
187 * @param dir The destination directory.
188 * @param src The source file.
189 * @param prefs The preferences for the destination file.
190 * @param finfo Information about the source file.
191 * @param pm Callback for status updates.
192 * @param isTemp True if the destination file is a temp file.
193 * @return The created file.
194 * @throws Exception
195 */
196 public static File convertFile(File dir, File src, FormatPrefs prefs, TocEntry finfo,
197 ProgressListener pm, boolean isTemp) throws Exception {
198 int srcFormat = getFileFormat(src);
199 // if no format change is required, just copy the file
200 if ((srcFormat == prefs.format) && !dir.getCanonicalPath().equals(src.getParent()))
201 return makeLocalCopy(dir, src, pm, isTemp);
202 //
203 switch(prefs.format) {
204 case FormatPrefs.MP3_FORMAT:
205 switch(srcFormat) {
206 case FormatPrefs.WAV_FORMAT:
207 return convertWAVToMP3(dir, src, finfo, prefs, pm, isTemp);
208 case FormatPrefs.OGG_FORMAT:
209 return convertOggToMP3(dir, src, finfo, prefs, pm, isTemp);
210 case FormatPrefs.MP3_FORMAT:
211 if (prefs.bitRate < finfo.rate)
212 return resampleMP3(dir, src, finfo, prefs, pm, isTemp);
213 }
214 break;
215 case FormatPrefs.WAV_FORMAT:
216 switch(srcFormat) {
217 case FormatPrefs.MP3_FORMAT:
218 return convertMP3ToWAV(dir, src, finfo, prefs, pm, isTemp);
219 case FormatPrefs.OGG_FORMAT:
220 return convertOggToWAV(dir, src, finfo, prefs, pm, isTemp);
221 }
222 break;
223 case FormatPrefs.OGG_FORMAT:
224 switch(srcFormat) {
225 case FormatPrefs.MP3_FORMAT:
226 return convertMP3ToOgg(dir, src, finfo, prefs, pm, isTemp);
227 case FormatPrefs.WAV_FORMAT:
228 return convertWAVToOgg(dir, src, finfo, prefs, pm, isTemp);
229 case FormatPrefs.OGG_FORMAT:
230 if (prefs.bitRate < finfo.rate)
231 return resampleOgg(dir, src, finfo, prefs, pm, isTemp);
232 }
233 break;
234 }
235 return makeLocalCopy(dir, src, pm, isTemp);
236 }
237 /**
238 * Creates any necessary subdirectories for the path.
239 *
240 * @param path the path.
241 * @throws IOException if directories cannot be created.
242 */
243 static public void createSubdirs(String path) throws IOException {
244 int ind = 0;
245 while((ind = path.indexOf(File.separator, ind + 1)) > 0) {
246 String p = path.substring(0, ind);
247 File d = new File(p);
248 if (!d.isDirectory()) {
249 if (d.mkdir() == false) throw new IOException("can't create directory: " + p);
250 }
251 }
252 }
253 /**
254 * Changes the sampling rate (or other characteristics) of an existing MP3 file.
255 *
256 * @param dir The destination directory in which to create the file.
257 * @param f The original OGG file.
258 * @param finfo Track information for the original file.
259 * @param prefs The preferences to use when creating the new file.
260 * @param pm Callback for status updates.
261 * @param isTemp If true, marks the file as tempoary (to be deleted on exit)
262 * @return the newly created file.
263 * @throws Exception if the converted file cannot be written.
264 */
265 public static File resampleMP3(File dir, File f, TocEntry finfo, FormatPrefs prefs,
266 ProgressListener pm, boolean isTemp) throws Exception {
267 return runNotLame(dir, f, finfo, prefs, pm, isTemp, true);
268 }
269 /**
270 * Changes the sampling rate (or other characteristics) of an existing OGG file.
271 *
272 * @param dir The destination directory in which to create the file.
273 * @param f The original OGG file.
274 * @param finfo Track information for the original file.
275 * @param prefs The preferences to use when creating the new file.
276 * @param pm Callback for status updates.
277 * @param isTemp If true, marks the file as tempoary (to be deleted on exit)
278 * @return the newly created file.
279 * @throws Exception if the converted file cannot be written.
280 */
281 public static File resampleOgg(File dir, File f, TocEntry finfo, FormatPrefs prefs,
282 ProgressListener pm, boolean isTemp) throws Exception {
283 return runWav2Ogg(dir, f, finfo, prefs, pm, isTemp);
284 }
285 /**
286 * Converts a file from OGG format to MP3 format.
287 *
288 * @param dir The destination directory in which to create the file.
289 * @param f The original OGG file.
290 * @param info Track information for the original file.
291 * @param prefs The preferences to use when creating the new file.
292 * @param pm Callback for status updates.
293 * @param isTemp If true, marks the file as tempoary (to be deleted on exit)
294 * @return the newly created file.
295 * @throws Exception if the converted file cannot be written.
296 */
297 public static File convertOggToMP3(File dir, File f, TocEntry info, FormatPrefs prefs,
298 ProgressListener pm, boolean isTemp) throws Exception {
299 File wf = convertOggToWAV(tempDir, f, info, prefs, pm, true);
300 if (wf == null) return null;
301 File retval = convertWAVToMP3(dir, wf, info, prefs, pm, isTemp);
302 wf.delete();
303 return retval;
304
305 }
306 /**
307 * Creates a new MP3 file based on an existing WAV file.
308 * @param dir The destination directory in which to create the file.
309 * @param f The WAV file to translate from.
310 * @param finfo Information about the source file.
311 * @param prefs The preferences to use when creating the new file.
312 * @param pm The status display object.
313 * @param isTemp If true, marks the file as tempoary (to be deleted on exit)
314 * @return The new MP3 file.
315 * @throws Exception if the converted file cannot be written.
316 */
317 public static File convertWAVToMP3(File dir, File f, TocEntry finfo, FormatPrefs prefs,
318 final ProgressListener pm, boolean isTemp) throws Exception {
319 return runNotLame(dir, f, finfo, prefs, pm, isTemp, false);
320 }
321 /**
322 * Creates a new WAV file based on an existing MP3 file.
323 * @param dir The destination directory in which to create the file.
324 * @param f The MP3 file to translate from.
325 * @param finfo Information about the source file.
326 * @param prefs The preferences to use when creating the new file.
327 * @param pm The status display object.
328 * @param isTemp If true, marks the file as tempoary (to be deleted on exit)
329 * @return The new WAV file.
330 * @throws Exception if the converted file cannot be written.
331 */
332 public static File convertMP3ToWAV(File dir, File f, TocEntry finfo, FormatPrefs prefs,
333 final ProgressListener pm, boolean isTemp) throws Exception {
334 return useMpg123 ? runMpg123(dir, f, finfo, prefs, pm, isTemp) : runMadplay(dir, f, finfo, prefs, pm, isTemp);
335 }
336 /**
337 * Creates a new WAV file based on an existing Ogg file.
338 * @param dir The destination directory in which to create the file.
339 * @param f The Ogg file to translate from.
340 * @param finfo Information about the source file.
341 * @param prefs The preferences to use when creating the new file.
342 * @param pm The status display object.
343 * @param isTemp If true, marks the file as tempoary (to be deleted on exit)
344 * @return The new WAV file.
345 * @throws Exception if the converted file cannot be written.
346 */
347 public static File convertOggToWAV(File dir, File f, TocEntry finfo, FormatPrefs prefs,
348 final ProgressListener pm, boolean isTemp) throws Exception {
349 String fileName = newPath(f.getName(), ".wav");
350 String destPath = dir.getCanonicalPath() + File.separator + fileName;
351 Vector cmd = Native.parseUserInput(ogg2wavCmd);
352 cmd.add("-o"); cmd.add(destPath);
353 cmd.add(f.getPath());
354 pm.setTotal(100);
355 createSubdirs(destPath);
356 int err = Native.runCommand(cmd, null, new Native.LineReader() {
357 public boolean processLine(String str, boolean e) {
358 if (pm.isCanceled()) return false;
359 try {
360 if (str.indexOf("[") != -1) {
361 int pct = Integer.parseInt(str.substring(2, 5).trim());
362 if (pct != cpct) {
363 pm.setCurrent(pct, -1, -1);
364 cpct = pct;
365 }
366 }
367 } catch (Exception ex) { }
368 return true;
369 }
370 int cpct = 0;
371 }, false);
372 if (err == 0)
373 pm.setCurrent(100, -1, -1);
374 File retval = new File(destPath);
375 if (err != 0) {
376 retval.delete();
377 return null;
378 }
379 return retval;
380 }
381 /**
382 * Creates a new Ogg file based on an existing MP3 file.
383 * @param dir The destination directory in which to create the file.
384 * @param f The MP3 file to translate from.
385 * @param finfo Information about the source file.
386 * @param prefs The preferences to use when creating the new file.
387 * @param pm The status display object.
388 * @param isTemp If true, marks the file as tempoary (to be deleted on exit)
389 * @return The new Ogg file.
390 * @throws Exception if the converted file cannot be written.
391 */
392 public static File convertMP3ToOgg(File dir, File f, TocEntry finfo, FormatPrefs prefs,
393 ProgressListener pm, boolean isTemp) throws Exception {
394 File wf = convertMP3ToWAV(tempDir, f, finfo, prefs, pm, true);
395 if (wf == null) return null;
396 File retval = convertWAVToOgg(dir, wf, finfo, prefs, pm, isTemp);
397 wf.delete();
398 return retval;
399 }
400 /**
401 * Creates a new Ogg file based on an existing WAV file.
402 * @param dir The destination directory in which to create the file.
403 * @param f The WAV file to translate from.
404 * @param finfo Information about the source file.
405 * @param prefs The preferences to use when creating the new file.
406 * @param pm The status display object.
407 * @param isTemp If true, marks the file as tempoary (to be deleted on exit)
408 * @return The new Ogg file.
409 * @throws Exception if the converted file cannot be written.
410 */
411 public static File convertWAVToOgg(File dir, File f, TocEntry finfo, FormatPrefs prefs,
412 final ProgressListener pm, boolean isTemp) throws Exception {
413 return runWav2Ogg(dir, f, finfo, prefs, pm, isTemp);
414 }
415 /**
416 *
417 * @param f
418 * @throws Exception
419 */
420 public static void normalize(File f) throws Exception {
421 Vector v = Native.parseUserInput(normalizeCmd);
422 v.add("-m");
423 BufferedReader rdr = null;
424 try {
425 rdr = new BufferedReader(new InputStreamReader(new FileInputStream(f)));
426 String str;
427 while((str = rdr.readLine()) != null) {
428 v.add(str);
429 Native.runCommand(v, null, new Native.LineReader() {
430 public boolean processLine(String str, boolean e) {
431 return true;
432 } }, true);
433 }
434 } finally {
435 if (rdr != null) rdr.close();
436 }
437 }
438 public static void setMpg123Cmd(String cmd) { mpg123Cmd = cmd; useMpg123 = true; }
439 public static void setMadplayCmd(String cmd) { madplayCmd = cmd; }
440 public static void setOgg2wavCmd(String cmd) { ogg2wavCmd = cmd; }
441 public static void setWav2oggCmd(String cmd) { wav2oggCmd = cmd; }
442 public static void setLameCmd(String cmd) { notLameCmd = cmd; }
443 public static void setNormalizeCmd(String cmd) { normalizeCmd = cmd; }
444 public static void setTempDir(File dir) { tempDir = dir; }
445 //
446 private static File runWav2Ogg(File dir, File f, TocEntry finfo, FormatPrefs prefs,
447 final ProgressListener pm, boolean isTemp) throws Exception {
448 Vector v = new Vector();
449 String fileName = newPath(f.getName(), ".ogg");
450 String destPath = dir.getCanonicalPath() + File.separator + fileName;
451 pm.setTotal(100);
452 pm.setCurrent(0, -1, -1);
453 v.add(wav2oggCmd);
454 addOggencArgs(v, finfo, prefs);
455 v.add("-o"); v.add(destPath);
456 v.add(f.getPath());
457 createSubdirs(destPath);
458 int err = Native.runCommand(v, null, new OggencReader(pm), false);
459 if (err == 0)
460 pm.setCurrent(100, -1, -1);
461 File retval = new File(destPath);
462 if (err != 0) {
463 retval.delete();
464 return null;
465 }
466 return retval;
467 }
468 private static void addOggencArgs(Vector v, TocEntry finfo, FormatPrefs prefs) {
469 v.add("-t"); v.add(finfo.title);
470 v.add("-a"); v.add(finfo.artist);
471 v.add("-l"); v.add(finfo.album);
472 v.add("-N"); v.add(Integer.toString(finfo.track));
473 if ((finfo.comment != null) && !finfo.comment.equals("")) { v.add("-c"); v.add(finfo.comment); }
474 if (finfo.genre != null) { v.add("-G"); v.add(finfo.genre); }
475 }
476 private static class OggencReader implements Native.LineReader {
477 public OggencReader(ProgressListener pm) { this.pm = pm; }
478 public boolean processLine(String str, boolean e) {
479 if (pm.isCanceled()) return false;
480 int n1 = str.indexOf("[");
481 if (n1 != -1) {
482 int n2 = str.indexOf(".", n1 + 1);
483 if (n2 != -1) {
484 try {
485 pm.setCurrent(Integer.parseInt(str.substring(n1 + 1, n2).trim()), -1, -1);
486 } catch (NumberFormatException ex) { }
487 }
488 }
489 return true;
490 }
491 private ProgressListener pm;
492 }
493 private static File runMpg123(File dir, File f, TocEntry finfo, FormatPrefs prefs,
494 final ProgressListener pm, boolean isTemp) throws Exception {
495 String fileName = newPath(f.getName(), ".wav");
496 String destPath = dir.getCanonicalPath() + File.separator + fileName;
497 Vector v = Native.parseUserInput(mpg123Cmd);
498 v.add("-v"); v.add("-w");
499 v.add(destPath);
500 v.add(f.getPath());
501 pm.setTotal(100);
502 createSubdirs(destPath);
503 int err = Native.runCommand(v, null, new Native.LineReader() {
504 public boolean processLine(String str, boolean err) {
505 if (pm.isCanceled()) return false;
506 // skip past everything until we see this
507 if (!inBody) {
508 if (str.startsWith("Audio:")) { inBody = true; }
509 return true;
510 }
511 // now pick out the progress indicator
512 int i1 = str.indexOf("["); if (i1 == -1) return true;
513 int i2 = str.indexOf("]", i1); if (i2 == -1) return true;
514 String ns = str.substring(i1 + 1, i2).trim();
515 if (ns.indexOf(":") != -1) return true;
516 int n = Integer.parseInt(ns);
517 if (!started)
518 mx = n;
519 cur = mx - n;
520 if ((mx != -1) && !started) {
521 pm.setTotal(mx);
522 started = true;
523 }
524 pm.setCurrent(cur, -1, -1);
525 return true;
526 }
527 int mx = -1, cur;
528 boolean inBody = false;
529 boolean started = false;
530 }, true);
531 if (err == 0)
532 pm.setCurrent(100, -1, -1);
533 File retval = new File(destPath);
534 if (err != 0) {
535 retval.delete();
536 return null;
537 }
538 return retval;
539 }
540 private static class NotLameReader implements Native.LineReader {
541 NotLameReader(ProgressListener pm) { this.pm = pm; }
542 public boolean processLine(String str, boolean e) {
543 if (str.equals("")) started = true;
544 if (pm.isCanceled()) return false;
545 int wh1 = str.indexOf("(");
546 int wh2 = str.indexOf(")");
547 if ((wh1 != -1) && (wh2 != -1) && started && (str.indexOf("average: ") == -1)) {
548 int pct = Integer.parseInt(str.substring(wh1 + 1, wh2 - 1).trim());
549 pm.setCurrent(pct, -1, -1);
550 }
551 return true;
552 }
553 private ProgressListener pm;
554 private boolean started = false;
555 }
556 private static void addNotLameArgs(Vector v, TocEntry finfo, FormatPrefs prefs, boolean isMP3) {
557 if (isMP3) v.add("--mp3input");
558 switch(prefs.quality) {
559 case FormatPrefs.LOW_QUALITY: v.add("-f"); break;
560 case FormatPrefs.HIGH_QUALITY: v.add("-h"); break;
561 case FormatPrefs.CUSTOM_QUALITY: v.add("-b"); v.add(Integer.toString(prefs.bitRate)); break;
562 }
563 v.add("--tt"); v.add(finfo.title);
564 v.add("--ta"); v.add(finfo.artist);
565 v.add("--tl"); v.add(finfo.album);
566 if (finfo.year != 0) { v.add("--ty"); v.add(Integer.toString(finfo.year)); }
567 if (finfo.comment != null) { v.add("--tc"); v.add(finfo.comment); }
568 if (finfo.genre != null) { v.add("--tg"); v.add(finfo.genre); }
569 if (finfo.track != 0) { v.add("--tn"); v.add(Integer.toString(finfo.track)); }
570 }
571 private static File runNotLame(File dir, File f, TocEntry finfo, FormatPrefs prefs,
572 final ProgressListener pm, boolean isTemp, boolean isMP3) throws Exception {
573 String fileName = newPath(f.getName(), ".mp3");
574 String destPath = dir.getCanonicalPath() + File.separator + fileName;
575 pm.setTotal(100);
576 Vector v = Native.parseUserInput(notLameCmd);
577 addNotLameArgs(v, finfo, prefs, isMP3);
578 v.add(f.getCanonicalPath());
579 v.add(destPath);
580 createSubdirs(destPath);
581 int err = Native.runCommand(v, null, new NotLameReader(pm), false);
582 if (err == 0)
583 pm.setCurrent(100, -1, -1);
584 File retval = new File(destPath);
585 if (err != 0) {
586 retval.delete();
587 return null;
588 }
589 return retval;
590 }
591 private static int parseSecs(String tm) {
592 int p1 = tm.indexOf(":");
593 String mins = tm.substring(0, p1);
594 String secs = tm.substring(p1 + 1);
595 return (Integer.parseInt(mins) * 60) + ((int) Float.parseFloat(secs));
596 }
597 private static String newPath(String f, String suffix) {
598 int wh = f.lastIndexOf(".");
599 return f.substring(0, wh) + suffix;
600 }
601 private static class NotlameVerRdr implements Native.LineReader {
602 public boolean processLine(String str, boolean e) {
603 if (!str.equals("")) {
604 versionString = str;
605 return false;
606 }
607 return true;
608 }
609 public String versionString;
610 }
611 private static String notlameVersion() {
612 Vector cmd = Native.parseUserInput(notLameCmd);
613 NotlameVerRdr rdr = new NotlameVerRdr();
614 try {
615 Native.runCommand(cmd, null, rdr, true);
616 } catch (Exception ex) {
617 return "notlame exception";
618 }
619 return rdr.versionString;
620 }
621 private static class MadplayVerRdr implements Native.LineReader {
622 public MadplayVerRdr() { versionString = null; }
623 public boolean processLine(String str, boolean e) {
624 if (versionString == null)
625 versionString = str;
626 return false;
627 }
628 String versionString;
629 }
630 private static class MadplayReader implements Native.LineReader {
631 public MadplayReader(ProgressListener pm) { this.pm = pm; }
632 public boolean processLine(String str, boolean e) {
633 if ((str.length() > 9) && (str.charAt(3) == ':') && (str.charAt(6) == ':')) {
634 int hr = Integer.parseInt(str.substring(1, 3));
635 int min = Integer.parseInt(str.substring(4, 6));
636 int sec = Integer.parseInt(str.substring(7, 9));
637 int secs = (((hr * 60) + min) * 60) + sec;
638 pm.setCurrent(secs, -1, -1);
639 }
640 return true;
641 }
642 ProgressListener pm;
643 }
644 private static String madplayVersion() {
645 Vector cmd = Native.parseUserInput(madplayCmd);
646 cmd.add("-V");
647 MadplayVerRdr rdr = new MadplayVerRdr();
648 try {
649 Native.runCommand(cmd, null, rdr, false);
650 } catch (Exception ex) {
651 return "madplay exception: " + ex;
652 }
653 return rdr.versionString;
654 }
655 private static File runMadplay(File dir, File f, TocEntry finfo, FormatPrefs prefs,
656 final ProgressListener pm, boolean isTemp) throws Exception {
657 Vector cmd = Native.parseUserInput(madplayCmd);
658 String fileName = newPath(f.getName(), ".wav");
659 String destPath = dir.getCanonicalPath() + File.separator + fileName;
660 cmd.add("-v");
661 cmd.add("--output=wave:" + destPath);
662 cmd.add(f.getPath());
663 createSubdirs(destPath);
664 pm.setTotal(finfo.len / 75);
665 pm.setCurrent(0, -1, -1);
666 int err = Native.runCommand(cmd, null, new MadplayReader(pm), false);
667 if (err == 0)
668 pm.setCurrent(100, -1, -1);
669 File retval = new File(destPath);
670 if (err != 0) {
671 retval.delete();
672 return null;
673 }
674 return retval;
675 }
676 private static class Mpg123VerRdr implements Native.LineReader {
677 public Mpg123VerRdr() { started = false; }
678 public boolean processLine(String str, boolean e) {
679 if (!started) {
680 started = true;
681 return true;
682 }
683 versionString = str;
684 return false;
685 }
686 private boolean started;
687 public String versionString;
688 }
689 private static String mpg123Version() {
690 Vector cmd = Native.parseUserInput(mpg123Cmd);
691 Mpg123VerRdr rdr = new Mpg123VerRdr();
692 try {
693 Native.runCommand(cmd, null, rdr, true);
694 } catch (Exception ex) {
695 return "mpg123 exception: " + ex.toString();
696 }
697 return "mpg123 " + rdr.versionString;
698 }
699 private static class OggencVerRdr implements Native.LineReader {
700 public boolean processLine(String str, boolean e) {
701 if (!str.equals("")) {
702 versionString = str;
703 return false;
704 }
705 return true;
706 }
707 public String versionString;
708 }
709 private static String oggencVersion() {
710 Vector cmd = Native.parseUserInput(wav2oggCmd);
711 OggencVerRdr rdr = new OggencVerRdr();
712 try {
713 Native.runCommand(cmd, null, rdr, true);
714 } catch (Exception ex) {
715 return "oggenc exception: " + ex.toString();
716 }
717 return rdr.versionString;
718 }
719 private static class Ogg123VerRdr implements Native.LineReader {
720 public boolean processLine(String str, boolean e) {
721 if (!str.equals("")) {
722 versionString = str;
723 return false;
724 }
725 return true;
726 }
727 public String versionString;
728 }
729 private static String ogg123Version() {
730 Vector cmd = Native.parseUserInput(ogg2wavCmd);
731 Ogg123VerRdr rdr = new Ogg123VerRdr();
732 try {
733 Native.runCommand(cmd, null, rdr, true);
734 } catch (Exception ex) {
735 return "ogg123 exception: " + ex.toString();
736 }
737 return rdr.versionString;
738 }
739 private static String truncateName(String str, int mx) {
740 if (str.length() > mx) {
741 str = str.substring(0, mx) + "...";
742 }
743 return str;
744 }
745 private static final int COPY_BUFSIZ = 32768;
746 private static String mpg123Cmd;
747 private static String madplayCmd;
748 private static String ogg2wavCmd;
749 private static String wav2oggCmd;
750 private static String notLameCmd;
751 private static String normalizeCmd;
752 private static File tempDir;
753 private static boolean useMpg123;
754 private static final int MP3_LOW_QUALITY_RATE = 8 * 1024 * 32;
755 private static final int MP3_MED_QUALITY_RATE = 16 * 1024 * 32;
756 private static final int MP3_HIGH_QUALITY_RATE = 24 * 1024 * 32;
757 private static final int OGG_LOW_QUALITY_RATE = 8 * 1024 * 32;
758 private static final int OGG_MED_QUALITY_RATE = 16 * 1024 * 32;
759 private static final int OGG_HIGH_QUALITY_RATE = 24 * 1024 * 32;
760 private static final int WAV_LOW_QUALITY_RATE = 8 * 1024 * 32;
761 private static final int WAV_MED_QUALITY_RATE = 16 * 1024 * 32;
762 private static final int WAV_HIGH_QUALITY_RATE = 24 * 1024 * 32;
763 private static final int WMA_LOW_QUALITY_RATE = 8 * 1024 * 32;
764 private static final int WMA_MED_QUALITY_RATE = 16 * 1024 * 32;
765 private static final int WMA_HIGH_QUALITY_RATE = 24 * 1024 * 32;
766 static {
767 mpg123Cmd = "mpg123";
768 ogg2wavCmd = "ogg123";
769 wav2oggCmd = "oggenc";
770 notLameCmd = "notlame";
771 normalizeCmd = "normalize";
772 madplayCmd = "madplay";
773 }
774 }
775
776 /*
777 Local Variables:
778 mode:java
779 indent-tabs-mode:nil
780 c-basic-offset:4
781 c-indent-level:4
782 c-continued-statement-offset:4
783 c-brace-offset:-4
784 c-brace-imaginary-offset:-4
785 c-argdecl-indent:0
786 c-label-offset:0
787 End:
788 */