Main Page   Class Hierarchy   Compound List   File List   Compound Members  

MEAPUtil.java

00001 /*
00002  *  Copyright 2006 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 
00042 import com.meapsoft.featextractors.AvgMelSpec;
00043 
00052 public abstract class MEAPUtil
00053 {
00054     String outFile;
00055     
00056     // Audio format parameters
00057     public static final int numChannels = 1;
00058     public static final int bitsPerSamp = 16;
00059     public static final int samplingRate = 44100;
00060     public static final boolean signed = true;
00061     public static final boolean bigEndian = false;
00062     public static final AudioFormat stereo = new AudioFormat(samplingRate, bitsPerSamp, 2, 
00063                                          signed, bigEndian);
00064     AudioFormat format = new AudioFormat(samplingRate, bitsPerSamp, 
00065                                          numChannels, signed, bigEndian);
00066 
00067     // Various numerical parameters
00068     public static final int frameSize = 256;
00069     public static final int frameLatency = 200;
00070     public static final int melFreqs = 40;
00071 
00072     public static int mixerToUse = 0;
00073     public static double hitsAlpha = 1;
00074     public static double bandFrac = 0.3;
00075     public static double bandThresh = 2.0;
00076 
00077 
00078     // All MEAPUtils need to implement this interface:
00079     // void setup()  (optional - this class implements an empty version)
00080     // void go()
00081     // MEAPFile doit(args) - the name and return arguments of this
00082     //                       function are not standardized since they all
00083     //                       perform different options - see the Composer
00084     //                       interface for a standard interface for Composers  
00085     //                       (i.e. Segmenter has FeatFile processAudioFile(String)
00086 
00087     public void setup() throws IOException, ParserException
00088         { }
00089     public abstract void go(); 
00090 
00091     public static void printCommandLineOptions(char arg)
00092     {
00093         switch(arg)
00094         {
00095         case 'f':
00096             System.out.println(
00097               "    -f feat_name    use feat_name features (defaults to AvgMelSpec, can have multiple -f arguments)\n" +
00098               "Supported feat_names are: " +
00099               "");
00100             RTSI.find("com.meapsoft.featextractors", "com.meapsoft.featextractors.FeatureExtractor");
00101             break;
00102         case 'd':
00103             System.out.println(
00104               "    -d dist_metric  distance metric to use (defaults to EuclideanDist, can string them together with multiple -d arguments)\n" +
00105               "Supported distance metrics are: " +
00106               "");
00107             // this doesn't work unless you have a package name...
00108             RTSI.find("com.meapsoft", "com.meapsoft.ChunkDist");
00109             break;
00110         case 'i':
00111             System.out.println(
00112               "    -i feat_dim     what feature dimentions to use (defaults to all)\n" +  
00113               "                    where feat_dim is a comma separated list (no spaces)\n" +
00114               "                    of integer indices and ranges (e.g. 1-5,7:9,11)" + 
00115               "");
00116             break;
00117         }
00118     }
00119 
00120     public static void printCommandLineOptions(char[] args)
00121     {
00122         for(int x = 0; x < args.length; x++)
00123             printCommandLineOptions(args[x]);
00124     }
00125 
00126 
00131     public static int[] parseFeatDim(String[] args, String argString)
00132     {
00133         Vector features = new Vector();
00134 
00135         Getopt opt = new Getopt("MEAPUtil", args, argString);
00136         opt.setOpterr(false);
00137         
00138         int c = -1;
00139         while ((c = opt.getopt()) != -1) 
00140         {
00141             if(c == 'i') 
00142             {
00143                 String[] dims = opt.getOptarg().split(",");
00144                 for(int x = 0; x < dims.length; x++)
00145                 {
00146                     String[] range = dims[x].split("[:-]",2);
00147                     int start = Integer.parseInt(range[0]);
00148                     features.add(new Integer(start));
00149 
00150                     if(range.length > 1)
00151                     {
00152                         int end = Integer.parseInt(range[1]);
00153                         for(int y = start+1; y <= end; y++)
00154                             features.add(new Integer(y));
00155                     }
00156                 }
00157             }
00158         }
00159 
00160         if(features.size() != 0)
00161         {
00162             //once again Java's fucked up attitude toward primitive
00163             //types bites me in the ass
00164             int[] featdim = new int[features.size()];
00165 
00166             for(int x = 0; x < featdim.length; x++)
00167                 featdim[x] = ((Integer)features.get(x)).intValue();
00168 
00169             return featdim;
00170         }
00171 
00172         return null;
00173     }
00174 
00175 
00179     public static ChunkDist parseChunkDist(String[] args, String argString, int[] featdim)
00180     {
00181         ChunkDist dist = null;
00182 
00183         Getopt opt = new Getopt("MEAPUtil", args, argString);
00184         opt.setOpterr(false);
00185         
00186         int c = -1;
00187         while ((c = opt.getopt()) != -1) 
00188         {
00189             if(c == 'd') 
00190             {
00191                 String className = opt.getOptarg();
00192 
00193                 // Try to load the class named className that extends
00194                 // ChunkDist.  (This is ugly as hell)
00195                 Class cl = null;
00196                 try 
00197                 { 
00198                     cl = Class.forName(className);
00199                 }
00200                 catch(ClassNotFoundException e)
00201                 { 
00202                     System.out.println(e);
00203                 }
00204 
00205                 try 
00206                 {
00207                     if(cl != null 
00208                        && Class.forName("ChunkDist").isAssignableFrom(cl))
00209                     {
00210                         try
00211                         {
00212                             //ChunkDist cd = (ChunkDist)cl.newInstance(featdim);
00213                             ChunkDist cd = null;
00214                             if(featdim == null)
00215                                  cd = (ChunkDist)cl.newInstance();
00216                             else
00217                             {
00218                                 // it is ridiculous how complicated it
00219                                 // is to get this done
00220                                 Class[] ctargs = new Class[1];
00221                                 Object arg = Array.newInstance(Integer.TYPE, 
00222                                                                featdim.length);
00223                                 ctargs[0] = arg.getClass();
00224                                 Constructor ct = cl.getConstructor(ctargs);
00225 
00226                                 for(int x = 0; x < featdim.length; x++)
00227                                     Array.setInt(arg, x, featdim[x]);
00228 
00229                                 Object[] o = new Object[1];
00230                                 o[0] = arg;
00231                                 cd = (ChunkDist)ct.newInstance(o);
00232                             }
00233                             
00234                             if(dist == null)
00235                                 dist = cd;
00236                             else
00237                             {
00238                                 // tack cd on to the end of the list
00239 
00240                                 // of ChunkDists specified in dist
00241                                 ChunkDist curr = dist;
00242                                 for(curr = dist; curr.next != null; curr = curr.next);
00243                                 curr.next = cd;
00244                             }
00245                         }
00246                         catch(Exception e)
00247                         {
00248                             System.out.println("Error constructing ChunkDist "
00249                                                + className);
00250                             e.printStackTrace();
00251                         }
00252                     }   
00253                     else
00254                         System.out.println("Ignoring unknown distance metric: " 
00255                                            + className);
00256                 }
00257                 catch(ClassNotFoundException e)
00258                 {
00259                     System.out.println("This should never ever happen....");
00260                     e.printStackTrace();
00261                 }
00262             }
00263         }
00264 
00265         // default
00266         if(dist == null)
00267             dist = new EuclideanDist(featdim);
00268 
00269         return dist;
00270     }
00271 
00272 
00276     public static Vector parseFeatureExtractor(String[] args)
00277     {
00278         return MEAPUtil.parseFeatureExtractor(args, "f:");
00279     }
00280 
00281 
00285     public static Vector parseFeatureExtractor(String[] args, String argString)
00286     {
00287         Vector featExts = new Vector();
00288 
00289         Getopt opt = new Getopt("MEAPUtil", args, argString);
00290         opt.setOpterr(false);
00291         
00292         int c = -1;
00293         while ((c = opt.getopt()) != -1) 
00294         {
00295             if(c == 'f')
00296             {
00297                 String featName = opt.getOptarg();
00298                 
00299                 // Try to load the class named featName that extends
00300                 // featextractors.FeatureExtractor.  (This is ugly as
00301                 // hell)
00302                 Class cl = null;
00303                 try 
00304                 { 
00305                     cl = Class.forName("com.meapsoft.featextractors." + featName);
00306                 }
00307                 catch(ClassNotFoundException e)
00308                 { 
00309                     try { cl = Class.forName(featName); }
00310                     catch(ClassNotFoundException e2)
00311                         { System.out.println(e2); }
00312                 }
00313 
00314                 try 
00315                 {
00316                     if(cl != null 
00317                        && Class.forName("com.meapsoft.featextractors.FeatureExtractor").isAssignableFrom(cl))
00318                     {
00319                         try
00320                         {
00321                             featExts.add(cl.newInstance());
00322                             //feat_names += featName + " ";
00323                         }
00324                         catch(Exception e)
00325                         {
00326                             System.out.println("Error constructing FeatureExtractor "
00327                                                + featName);
00328                             e.printStackTrace();
00329                         }
00330                     }   
00331                     else
00332                         System.out.println("Ignoring unknown feature: " + featName);
00333                 }
00334                 catch(ClassNotFoundException e)
00335                 {
00336                     System.out.println("This should never ever happen....");
00337                     e.printStackTrace();
00338                 }
00339             }
00340         }
00341 
00342         // default to AvgMelSpec
00343         if(featExts.size() == 0)
00344             featExts.add(new AvgMelSpec());
00345 
00346         return featExts;
00347     }
00348 
00349     public AudioWriter openAudioWriter() {
00350         SourceDataLine line = null;
00351         AudioWriter writer = null;
00352         DataLine.Info info = new DataLine.Info(SourceDataLine.class,
00353                                                format); 
00354         
00355         Mixer.Info[] m1x0rs = AudioSystem.getMixerInfo();
00356         System.out.println("Do you support " + info + "?");
00357         for(int i=0; i<m1x0rs.length; i++)
00358             if(AudioSystem.getMixer(m1x0rs[i]).isLineSupported(info))
00359                 System.out.println("("+i+") " + m1x0rs[i] + ": yes ");
00360             else
00361                 System.out.println("("+i+") " + m1x0rs[i] + ": no ");
00362         
00363         // Obtain and open the line.
00364         try { 
00365             line = (SourceDataLine) AudioSystem.getMixer(m1x0rs[mixerToUse])
00366                 .getLine(info); 
00367             line.open(format, 1024*5); 
00368             System.out.println("Source line opened from mixer: " 
00369                                + m1x0rs[mixerToUse]); 
00370             writer = new AudioWriter(line);
00371         } catch (LineUnavailableException ex) { 
00372             ex.printStackTrace(); 
00373         }
00374         
00375         return writer;
00376     }
00377     
00378     public AudioWriter openAudioWriter(String filename) {
00379         if(filename == null)
00380             return openAudioWriter();
00381         
00382         AudioWriter writer = null;
00383         File file = new File(filename);
00384         AudioFileFormat.Type targetType = AudioFileFormat.Type.WAVE;
00385         String extension = null;
00386         
00387         // Figure out the file extension
00388         int dotPosition = filename.lastIndexOf('.');
00389         if (dotPosition != -1)
00390             extension = filename.substring(dotPosition + 1);
00391         
00392         if(extension != null) {
00393             // Figure out the corresponding file type
00394             AudioFileFormat.Type[] aTypes = AudioSystem.getAudioFileTypes();
00395             for (int i = 0; i < aTypes.length; i++)
00396                 if (aTypes[i].getExtension().equals(extension))
00397                     targetType = aTypes[i];
00398         }
00399         
00400         try {
00401             writer = new AudioWriter(file, format, targetType);
00402         } catch(IOException ioe) {
00403             ioe.printStackTrace();
00404         }
00405         
00406         return writer;
00407     }
00408     
00409     // Get an input stream from an audio file
00410     public AudioInputStream openInputStream(String filename)
00411     {
00412         return openInputStream(filename, format);
00413     }
00414 
00415     // Get an input stream from an audio file
00416     public static AudioInputStream openInputStream(String filename, AudioFormat format) 
00417     {
00418         AudioInputStream stream = null;
00419  
00420         try {
00421             stream = AudioSystem.getAudioInputStream(new File(filename));
00422             //System.out.println(stream.getFormat());
00423 
00424             // convert to stereo PCM before converting to mono -
00425             // without this line the conversion process will crap out
00426             // on stereo MP3s.  But the conversion from mono to stereo
00427             // is unsopported for some reason...
00428             if(stream.getFormat().getChannels() >= 2)
00429                 stream = AudioSystem.getAudioInputStream(MEAPUtil.stereo, stream);
00430 
00431                         stream = AudioSystem.getAudioInputStream(format, stream);
00432         } catch(Exception e) {
00433                 System.out.println("got exception...");
00434             e.printStackTrace();
00435         }
00436 
00437         //System.out.println(stream.getFormat());
00438         
00439         return stream;
00440     }
00441 
00442     // Convert a byte stream into a stream of doubles.  If it's stereo,
00443     // the channels will be interleaved with each other in the double
00444     // stream, as in the byte stream.
00445     public static void bytes2doubles(byte[] audioBytes, double[] audioData, AudioFormat format) 
00446      {
00447          if (format.getSampleSizeInBits() == 16) 
00448          {
00449             if (format.isBigEndian()) 
00450             {
00451                 for (int i = 0; i < audioData.length; i++) 
00452                 {
00453                     /* First byte is MSB (high order) */
00454                     int MSB = (int) audioBytes[2*i];
00455                     /* Second byte is LSB (low order) */
00456                     int LSB = (int) audioBytes[2*i+1];
00457                     audioData[i] = ((double)(MSB << 8 | (255 & LSB))) 
00458                         / 32768.0;
00459                 }
00460             } 
00461             else 
00462             {
00463                 for (int i = 0; i < audioData.length; i++) 
00464                 {
00465                     /* First byte is LSB (low order) */
00466                     int LSB = (int) audioBytes[2*i];
00467                     /* Second byte is MSB (high order) */
00468                     int MSB = (int) audioBytes[2*i+1];
00469                     audioData[i] = ((double)(MSB << 8 | (255 & LSB))) 
00470                         / 32768.0;
00471                 }
00472             }
00473          } 
00474          else if (format.getSampleSizeInBits() == 8) 
00475          {
00476              int nlengthInSamples = audioBytes.length;
00477              if (format.getEncoding().toString().startsWith("PCM_SIGN")) 
00478              {
00479                  for (int i = 0; i < audioBytes.length; i++) 
00480                      audioData[i] = audioBytes[i] / 128.0;
00481              } 
00482              else 
00483              {
00484                  for (int i = 0; i < audioBytes.length; i++) 
00485                      audioData[i] = (audioBytes[i] - 128) / 128.0;
00486              }
00487          }
00488      }
00489     
00490     // Convert an array of doubles into a  byte stream
00491     public static void doubles2bytes(double[] audioData, byte[] audioBytes, AudioFormat format) 
00492     {
00493         int in;
00494         if (format.getSampleSizeInBits() == 16) 
00495         {
00496             if (format.isBigEndian()) 
00497             {
00498                 for (int i = 0; i < audioData.length; i++) 
00499                 {
00500                     in = (int)(audioData[i]*32767);
00501                     /* First byte is MSB (high order) */
00502                     audioBytes[2*i] = (byte)(in >> 8);
00503                     /* Second byte is LSB (low order) */
00504                     audioBytes[2*i+1] = (byte)(in & 255);
00505                 }
00506             } 
00507             else 
00508             {
00509                 for (int i = 0; i < audioData.length; i++) 
00510                 {
00511                     in = (int)(audioData[i]*32767);
00512                     /* First byte is LSB (low order) */
00513                     audioBytes[2*i] = (byte)(in & 255);
00514                     /* Second byte is MSB (high order) */
00515                     audioBytes[2*i+1] = (byte)(in >> 8);
00516                 }
00517             }
00518         } 
00519         else if (format.getSampleSizeInBits() == 8) 
00520         {
00521             if (format.getEncoding().toString().startsWith("PCM_SIGN")) 
00522             {
00523                 for (int i = 0; i < audioData.length; i++) 
00524                     audioBytes[i] = (byte)(audioData[i]*127);
00525             } 
00526             else 
00527             {
00528                 for (int i = 0; i < audioData.length; i++)
00529                     audioBytes[i] = (byte)(audioData[i]*127 + 127);
00530             }
00531         }
00532     }
00533 }
00534 

Generated on Thu May 11 15:04:10 2006 for MEAPsoft by doxygen1.2.18