Main Page   Packages   Class Hierarchy   Compound List   File List   Compound Members  

MEAPUtil.java

00001 /*
00002  *  Copyright 2006-2007 Columbia University.
00003  *
00004  *  This file is part of MEAPsoft.
00005  *
00006  *  MEAPsoft is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License version 2 as
00008  *  published by the Free Software Foundation.
00009  *
00010  *  MEAPsoft is distributed in the hope that it will be useful, but
00011  *  WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013  *  General Public License for more details.
00014  *
00015  *  You should have received a copy of the GNU General Public License
00016  *  along with MEAPsoft; if not, write to the Free Software
00017  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
00018  *  02110-1301 USA
00019  *
00020  *  See the file "COPYING" for the text of the license.
00021  */
00022 
00023 package com.meapsoft;
00024 
00025 import gnu.getopt.Getopt;
00026 
00027 import java.io.File;
00028 import java.io.IOException;
00029 import java.lang.reflect.Array;
00030 import java.lang.reflect.Constructor;
00031 import java.util.Vector;
00032 
00033 import javax.sound.sampled.AudioFileFormat;
00034 import javax.sound.sampled.AudioFormat;
00035 import javax.sound.sampled.AudioInputStream;
00036 import javax.sound.sampled.AudioSystem;
00037 import javax.sound.sampled.DataLine;
00038 import javax.sound.sampled.LineUnavailableException;
00039 import javax.sound.sampled.Mixer;
00040 import javax.sound.sampled.SourceDataLine;
00041 import javax.sound.sampled.UnsupportedAudioFileException;
00042 
00043 import javax.swing.BoundedRangeModel;
00044 import javax.swing.DefaultBoundedRangeModel;
00045 
00046 import util.RTSI;
00047 import com.meapsoft.featextractors.AvgMelSpec;
00048 
00057 public abstract class MEAPUtil implements Runnable
00058 {
00059     public static final String version = "1.1.1";
00060         
00061     // output file name
00062     String outFile;
00063     
00064     // slash
00065         public static final String slash = System.getProperty("file.separator");
00066         
00067         // error messages
00068         //public static final int FATAL_ERROR = 0;
00069         //public static final int MESSAGE = 1;
00070         
00071     // Audio format parameters
00072     public static final int numChannels = 1;
00073     public static final int bitsPerSamp = 16;
00074     public static final int samplingRate = 44100;
00075     //public static final int samplingRate = 22050;
00076     public static final boolean signed = true;
00077     public static final boolean bigEndian = false;
00078     public static final AudioFormat stereo = new AudioFormat(samplingRate, 
00079                                                              bitsPerSamp, 2, 
00080                                                              signed, bigEndian);
00081 
00082     AudioFormat format = new AudioFormat(samplingRate, bitsPerSamp, 
00083                                          numChannels, signed, bigEndian);
00084 
00085     // Various "global" numerical parameters:
00086     public static final int frameLatency = 200;
00087  
00088     public static int mixerToUse = 0;
00089 
00090     // Should we write the output MEAPFile(s)?  - not always necessary
00091     // when using the GUI.  Defaults to true.
00092     public boolean writeMEAPFile = true;
00093 
00094     // Should we print verbose output?  
00095     // Default is false because this might be called from a GUI. 
00096     protected boolean verbose = false;
00097 
00098     // keep track of this MEAPsoft utlity's progress
00099     protected BoundedRangeModel progress = new DefaultBoundedRangeModel();
00100     
00101     protected static ExceptionHandler exceptionHandler = new ExceptionHandler();
00102 
00103 
00104     // All MEAPUtils need to implement this interface:
00105     // void setup()  (optional - this class implements an empty version)
00106     // void run()
00107 
00108     public void setup() throws IOException, ParserException
00109         { }
00110 
00114     public abstract void run(); 
00115 
00116     public static void printCommandLineOptions(char arg)
00117     {
00118         switch(arg)
00119         {
00120         case 'f':
00121             System.out.println(
00122               "    -f feat_name    use feat_name features (defaults to AvgMelSpec, can have multiple -f arguments)\n" +
00123               "Supported feat_names are: " +
00124               "");
00125             RTSI.find("com.meapsoft.featextractors", "com.meapsoft.featextractors.FeatureExtractor");
00126             break;
00127         case 'd':
00128             System.out.println(
00129               "    -d dist_metric  distance metric to use (defaults to EuclideanDist, can string them together with multiple -d arguments)\n" +
00130               "Supported distance metrics are: " +
00131               "");
00132             // this doesn't work unless you have a package name...
00133             RTSI.find("com.meapsoft", "com.meapsoft.ChunkDist");
00134             break;
00135         case 'i':
00136             System.out.println(
00137               "    -i feat_dim     what feature dimentions to use (defaults to all)\n" +  
00138               "                    where feat_dim is a comma separated list (no spaces)\n" +
00139               "                    of integer indices and ranges (e.g. 1-5,7:9,11)" + 
00140               "");
00141             break;
00142         }
00143     }
00144 
00145     public static void printCommandLineOptions(char[] args)
00146     {
00147         for(int x = 0; x < args.length; x++)
00148             printCommandLineOptions(args[x]);
00149     }
00150 
00151 
00156     public static int[] parseFeatDim(String[] args, String argString)
00157     {
00158         Vector features = new Vector();
00159 
00160         Getopt opt = new Getopt("MEAPUtil", args, argString);
00161         opt.setOpterr(false);
00162         
00163         int c = -1;
00164         while ((c = opt.getopt()) != -1) 
00165         {
00166             if(c == 'i') 
00167             {
00168                 String[] dims = opt.getOptarg().split(",");
00169                 for(int x = 0; x < dims.length; x++)
00170                 {
00171                     String[] range = dims[x].split("[:-]",2);
00172                     int start = Integer.parseInt(range[0]);
00173                     features.add(new Integer(start));
00174 
00175                     if(range.length > 1)
00176                     {
00177                         int end = Integer.parseInt(range[1]);
00178                         for(int y = start+1; y <= end; y++)
00179                             features.add(new Integer(y));
00180                     }
00181                 }
00182             }
00183         }
00184 
00185         if(features.size() != 0)
00186         {
00187             //once again Java's fucked up attitude toward primitive
00188             //types bites me in the ass
00189             int[] featdim = new int[features.size()];
00190 
00191             for(int x = 0; x < featdim.length; x++)
00192                 featdim[x] = ((Integer)features.get(x)).intValue();
00193 
00194             return featdim;
00195         }
00196 
00197         return null;
00198     }
00199 
00200 
00204     public static ChunkDist parseChunkDist(String[] args, String argString, int[] featdim)
00205     {
00206         ChunkDist dist = null;
00207 
00208         Getopt opt = new Getopt("MEAPUtil", args, argString);
00209         opt.setOpterr(false);
00210         
00211         int c = -1;
00212         while ((c = opt.getopt()) != -1) 
00213         {
00214             if(c == 'd') 
00215             {
00216                 String className = opt.getOptarg();
00217 
00218                 // Try to load the class named className that extends
00219                 // ChunkDist.  (This is ugly as hell)
00220                 Class cl = null;
00221                 try 
00222                 { 
00223                     cl = Class.forName(className);
00224                 }
00225                 catch(ClassNotFoundException e)
00226                 { 
00227                     System.out.println(e);
00228                 }
00229 
00230                 try 
00231                 {
00232                     if(cl != null 
00233                        && Class.forName("ChunkDist").isAssignableFrom(cl))
00234                     {
00235                         try
00236                         {
00237                             //ChunkDist cd = (ChunkDist)cl.newInstance(featdim);
00238                             ChunkDist cd = null;
00239                             if(featdim == null)
00240                                  cd = (ChunkDist)cl.newInstance();
00241                             else
00242                             {
00243                                 // it is ridiculous how complicated it
00244                                 // is to get this done
00245                                 Class[] ctargs = new Class[1];
00246                                 Object arg = Array.newInstance(Integer.TYPE, 
00247                                                                featdim.length);
00248                                 ctargs[0] = arg.getClass();
00249                                 Constructor ct = cl.getConstructor(ctargs);
00250 
00251                                 for(int x = 0; x < featdim.length; x++)
00252                                     Array.setInt(arg, x, featdim[x]);
00253 
00254                                 Object[] o = new Object[1];
00255                                 o[0] = arg;
00256                                 cd = (ChunkDist)ct.newInstance(o);
00257                             }
00258                             
00259                             if(dist == null)
00260                                 dist = cd;
00261                             else
00262                             {
00263                                 // tack cd on to the end of the list
00264 
00265                                 // of ChunkDists specified in dist
00266                                 ChunkDist curr = dist;
00267                                 for(curr = dist; curr.next != null; curr = curr.next);
00268                                 curr.next = cd;
00269                             }
00270                         }
00271                         catch(Exception e)
00272                         {
00273                             System.out.println("Error constructing ChunkDist "
00274                                                + className);
00275                             e.printStackTrace();
00276                         }
00277                     }   
00278                     else
00279                         System.out.println("Ignoring unknown distance metric: " 
00280                                            + className);
00281                 }
00282                 catch(ClassNotFoundException e)
00283                 {
00284                     System.out.println("This should never ever happen....");
00285                     e.printStackTrace();
00286                 }
00287             }
00288         }
00289 
00290         // default
00291         if(dist == null)
00292             dist = new EuclideanDist(featdim);
00293 
00294         return dist;
00295     }
00296 
00297 
00301     public static Vector parseFeatureExtractor(String[] args)
00302     {
00303         return MEAPUtil.parseFeatureExtractor(args, "f:");
00304     }
00305 
00306 
00310     public static Vector parseFeatureExtractor(String[] args, String argString)
00311     {
00312         Vector featExts = new Vector();
00313 
00314         Getopt opt = new Getopt("MEAPUtil", args, argString);
00315         opt.setOpterr(false);
00316         
00317         int c = -1;
00318         while ((c = opt.getopt()) != -1) 
00319         {
00320             if(c == 'f')
00321             {
00322                 String featName = opt.getOptarg();
00323                 
00324                 // Try to load the class named featName that extends
00325                 // featextractors.FeatureExtractor.  (This is ugly as
00326                 // hell)
00327                 Class cl = null;
00328                 try 
00329                 { 
00330                     cl = Class.forName("com.meapsoft.featextractors." + featName);
00331                 }
00332                 catch(ClassNotFoundException e)
00333                 { 
00334                     try { cl = Class.forName(featName); }
00335                     catch(ClassNotFoundException e2)
00336                         { System.out.println(e2); }
00337                 }
00338 
00339                 try 
00340                 {
00341                     if(cl != null 
00342                        && Class.forName("com.meapsoft.featextractors.FeatureExtractor").isAssignableFrom(cl))
00343                     {
00344                         try
00345                         {
00346                             featExts.add(cl.newInstance());
00347                             //feat_names += featName + " ";
00348                         }
00349                         catch(Exception e)
00350                         {
00351                             System.out.println("Error constructing FeatureExtractor "
00352                                                + featName);
00353                             e.printStackTrace();
00354                         }
00355                     }   
00356                     else
00357                         System.out.println("Ignoring unknown feature: " + featName);
00358                 }
00359                 catch(ClassNotFoundException e)
00360                 {
00361                     System.out.println("This should never ever happen....");
00362                     e.printStackTrace();
00363                 }
00364             }
00365         }
00366 
00367         // default to AvgMelSpec
00368         if(featExts.size() == 0)
00369             featExts.add(new AvgMelSpec());
00370 
00371         return featExts;
00372     }
00373 
00374     public AudioWriter openAudioWriter() throws LineUnavailableException {
00375         SourceDataLine line = null;
00376         AudioWriter writer = null;
00377         DataLine.Info info = new DataLine.Info(SourceDataLine.class,
00378                                                format); 
00379         
00380         Mixer.Info[] m1x0rs = AudioSystem.getMixerInfo();
00381         //System.out.println("Do you support " + info + "?");
00382         for(int i=0; i<m1x0rs.length; i++)
00383             if(AudioSystem.getMixer(m1x0rs[i]).isLineSupported(info))
00384                 mixerToUse = i;
00385         
00386         // Obtain and open the line.
00387         line = (SourceDataLine) AudioSystem.getMixer(m1x0rs[mixerToUse])
00388             .getLine(info); 
00389         //line.open(format, 1024*5); 
00390         line.open(format);
00391         //System.out.println("Source line opened from mixer: " 
00392         //+ m1x0rs[mixerToUse]); 
00393         writer = new AudioWriter(line);
00394         
00395         return writer;
00396     }
00397     
00398     public AudioWriter openAudioWriter(String filename) throws LineUnavailableException, IOException {
00399         if(filename == null)
00400             return openAudioWriter();
00401         
00402         AudioWriter writer = null;
00403         File file = new File(filename);
00404         AudioFileFormat.Type targetType = AudioFileFormat.Type.WAVE;
00405         String extension = null;
00406         
00407         // Figure out the file extension
00408         int dotPosition = filename.lastIndexOf('.');
00409         if (dotPosition != -1)
00410             extension = filename.substring(dotPosition + 1);
00411         
00412         if(extension != null) {
00413             // Figure out the corresponding file type
00414             AudioFileFormat.Type[] aTypes = AudioSystem.getAudioFileTypes();
00415             for (int i = 0; i < aTypes.length; i++)
00416                 if (aTypes[i].getExtension().equals(extension))
00417                     targetType = aTypes[i];
00418         }
00419         
00420         writer = new AudioWriter(file, format, targetType);
00421         
00422         return writer;
00423     }
00424     
00425     // Get an input stream from an audio file
00426     public AudioInputStream openInputStream(String filename) throws IOException, UnsupportedAudioFileException
00427     {
00428         return openInputStream(filename, format);
00429     }
00430 
00431     // Get an input stream from an audio file
00432     public static AudioInputStream openInputStream(String filename, AudioFormat format) throws IOException, UnsupportedAudioFileException
00433     {
00434         AudioInputStream stream = null;
00435 
00436         stream = AudioSystem.getAudioInputStream(new File(filename));
00437         //System.out.println(stream.getFormat());
00438         
00439         AudioFormat baseFormat = stream.getFormat();
00440         AudioFormat decodedFormat = 
00441             new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
00442                             baseFormat.getSampleRate(),
00443                             16,
00444                             baseFormat.getChannels(),
00445                             baseFormat.getChannels() * 2,
00446                             baseFormat.getSampleRate(),
00447                             bigEndian);
00448         stream = AudioSystem.getAudioInputStream(decodedFormat, stream);
00449         //System.out.println(stream.getFormat());
00450 
00451         // convert to stereo PCM before converting to mono -
00452         // without this line the conversion process will crap out
00453         // on stereo MP3s.  But the conversion from mono to stereo
00454         // is unsupported for some reason...
00455         if(stream.getFormat().getChannels() >= 2)
00456             //stream = AudioSystem.getAudioInputStream(MEAPUtil.stereo, stream);
00457             stream = AudioSystem.getAudioInputStream(
00458                 new AudioFormat(format.getSampleRate(), bitsPerSamp, 2, 
00459                                 signed, bigEndian), 
00460                 stream);
00461         
00462         stream = AudioSystem.getAudioInputStream(format, stream);
00463 
00464         //System.out.println("final: "+stream.getFormat());
00465         
00466         return stream;
00467     }
00468 
00469     // Convert a byte stream into a stream of doubles.  If it's stereo,
00470     // the channels will be interleaved with each other in the double
00471     // stream, as in the byte stream.
00472     public static void bytes2doubles(byte[] audioBytes, double[] audioData, AudioFormat format) 
00473      {
00474          if (format.getSampleSizeInBits() == 16) 
00475          {
00476             if (format.isBigEndian()) 
00477             {
00478                 for (int i = 0; i < audioData.length; i++) 
00479                 {
00480                     /* First byte is MSB (high order) */
00481                     int MSB = (int) audioBytes[2*i];
00482                     /* Second byte is LSB (low order) */
00483                     int LSB = (int) audioBytes[2*i+1];
00484                     audioData[i] = ((double)(MSB << 8 | (255 & LSB))) 
00485                         / 32768.0;
00486                 }
00487             } 
00488             else 
00489             {
00490                 for (int i = 0; i < audioData.length; i++) 
00491                 {
00492                     /* First byte is LSB (low order) */
00493                     int LSB = (int) audioBytes[2*i];
00494                     /* Second byte is MSB (high order) */
00495                     int MSB = (int) audioBytes[2*i+1];
00496                     audioData[i] = ((double)(MSB << 8 | (255 & LSB))) 
00497                         / 32768.0;
00498                 }
00499             }
00500          } 
00501          else if (format.getSampleSizeInBits() == 8) 
00502          {
00503              int nlengthInSamples = audioBytes.length;
00504              if (format.getEncoding().toString().startsWith("PCM_SIGN")) 
00505              {
00506                  for (int i = 0; i < audioBytes.length; i++) 
00507                      audioData[i] = audioBytes[i] / 128.0;
00508              } 
00509              else 
00510              {
00511                  for (int i = 0; i < audioBytes.length; i++) 
00512                      audioData[i] = (audioBytes[i] - 128) / 128.0;
00513              }
00514          }
00515      }
00516     
00517     // Convert an array of doubles into a  byte stream
00518     public static void doubles2bytes(double[] audioData, byte[] audioBytes, AudioFormat format) 
00519     {
00520         int in;
00521         if (format.getSampleSizeInBits() == 16) 
00522         {
00523             if (format.isBigEndian()) 
00524             {
00525                 for (int i = 0; i < audioData.length; i++) 
00526                 {
00527                     in = (int)(audioData[i]*32767);
00528                     /* First byte is MSB (high order) */
00529                     audioBytes[2*i] = (byte)(in >> 8);
00530                     /* Second byte is LSB (low order) */
00531                     audioBytes[2*i+1] = (byte)(in & 255);
00532                 }
00533             } 
00534             else 
00535             {
00536                 for (int i = 0; i < audioData.length; i++) 
00537                 {
00538                     in = (int)(audioData[i]*32767);
00539                     /* First byte is LSB (low order) */
00540                     audioBytes[2*i] = (byte)(in & 255);
00541                     /* Second byte is MSB (high order) */
00542                     audioBytes[2*i+1] = (byte)(in >> 8);
00543                 }
00544             }
00545         } 
00546         else if (format.getSampleSizeInBits() == 8) 
00547         {
00548             if (format.getEncoding().toString().startsWith("PCM_SIGN")) 
00549             {
00550                 for (int i = 0; i < audioData.length; i++) 
00551                     audioBytes[i] = (byte)(audioData[i]*127);
00552             } 
00553             else 
00554             {
00555                 for (int i = 0; i < audioData.length; i++)
00556                     audioBytes[i] = (byte)(audioData[i]*127 + 127);
00557             }
00558         }
00559     }
00560 
00561     public void setExceptionHandler(ExceptionHandler eh)
00562     {
00563         exceptionHandler = eh;
00564     }
00565 
00570     public BoundedRangeModel getProgress()
00571     {
00572         return progress;
00573     }
00574     
00575         public static String[] getPaths()
00576         {
00577                 String meapsoftDirectory = null;
00578                 String dataDirectory = null;
00579                 
00580                 try
00581                 {
00582                         //This method will get the path to the jar or class file no-matter 
00583                         //where the jar is called from.
00584         
00585                         String javaclasspath = System.getProperty("java.class.path")
00586                                 .split(System.getProperty("path.separator"))[0];
00587         
00588                         File binPath = new File(javaclasspath);
00589         
00590                         binPath = binPath.getCanonicalFile().getParentFile();
00591                         //System.out.println("Path: " + binPath.toString()); 
00592                                         
00593                         meapsoftDirectory = binPath.getParent();
00594                         dataDirectory = meapsoftDirectory + slash + "data";
00595                         
00596                         //System.out.println("meapsoftDirectory: " + meapsoftDirectory + " dataDirectory: " + dataDirectory);
00597                 }
00598                 catch (Exception e)
00599                 {
00600                         System.out.println("problem getting paths! " + e.toString());
00601                         //ShowDialog(e, "", FATAL_ERROR);
00602                         return null;
00603                 }       
00604                 
00605                 String[] paths = {meapsoftDirectory, dataDirectory};
00606                 return paths;
00607         }
00608 }
00609 

Generated on Tue Feb 6 19:02:27 2007 for MEAPsoft by doxygen1.2.18