Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

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 */