Tuesday, July 10, 2012

Build Java connection with praat voice analyzer

  1. package mpi.eudico.client.annotator.viewer;  
  2.   
  3. import mpi.eudico.client.annotator.Constants;  
  4. import mpi.eudico.client.annotator.ElanLocale;  
  5.   
  6. import mpi.util.LogUtil;  
  7.   
  8. import java.io.BufferedInputStream;  
  9. import java.io.BufferedReader;  
  10. import java.io.File;  
  11. import java.io.FileInputStream;  
  12. import java.io.FileNotFoundException;  
  13. import java.io.FileOutputStream;  
  14. import java.io.FileWriter;  
  15. import java.io.IOException;  
  16. import java.io.InputStreamReader;  
  17. import java.io.ObjectInputStream;  
  18. import java.io.ObjectOutputStream;  
  19.   
  20. import java.net.URL;  
  21.   
  22. import java.util.Hashtable;  
  23. import java.util.logging.Logger;  
  24.   
  25. import javax.swing.JFileChooser;  
  26. import javax.swing.JFrame;  
  27. import javax.swing.JOptionPane;  
  28.   
  29.   
  30. /** 
  31.  * A class to start the Praat executable and open the .wav file that  has been 
  32.  * loaded into Elan.<br> 
  33.  * Opening a file and (possibly) selecting a part from that file is a two step 
  34.  * process. First we have to make sure that Praat is/has been started, next we 
  35.  * have to use the sendpraat executable to let Praat execute a script with the 
  36.  * right arguments. At least on Windows it is not possible to start Praat 
  37.  * with  the script etc. as arguments. The Windows praatcon.exe Praat console 
  38.  * executable  can start Praat with a script etc., but it can not load a 'long 
  39.  * sound' and create  a view and editor for it.<br> 
  40.  * Since elan only loads sound files that are on a local file system it is 
  41.  * save to expect that a provided url is on the local file system. 
  42.  * 
  43.  * @author Han Sloetjes 
  44.  * @version jul 2004 
  45.  */  
  46. public class PraatConnection {  
  47.     /** constant for the name of the praat sript */  
  48.     private static final String PRAAT_SCRIPT = "openpraat.praat";  
  49.   
  50.     /** constant for a script to create a clip from a sound file */  
  51.     public static final String PRAAT_CLIP_SCRIPT = "createsoundclip.praat";  
  52.     private static String scriptFileName;  
  53.   
  54.     /** constant for the Praat application path property */  
  55.     private static final String PRAAT_APP = "Praat app";  
  56.   
  57.     /** constant for the sendpraat application path property */  
  58.     private static final String SENDPRAAT_APP = "Sendpraat app";  
  59.   
  60.     /** Holds value of property DOCUMENT ME! */  
  61.     private static final String PRAAT_PREFS_FILE = Constants.ELAN_DATA_DIR +  
  62.         Constants.FILESEPARATOR + "praat.pfs";  
  63.   
  64.     /** 
  65.      * a non-thread-safe way of temporarily storing the result of a check on 
  66.      * the  running state of Praat (Unix systems) 
  67.      */  
  68.     static boolean isPraatRunning = false;  
  69.   
  70.     /** the logger */  
  71.     private static final Logger LOG = Logger.getLogger(PraatConnection.class.getName());  
  72.     private static Hashtable preferences;  
  73.   
  74.     /** 
  75.      * Creates a new PraatConnection instance 
  76.      */  
  77.     private PraatConnection() {  
  78.     }  
  79.   
  80.     /** 
  81.      * Asynchronously opens a file in Praat. 
  82.      * 
  83.      * @param fileName the media file 
  84.      * @param begintime the selection begintime 
  85.      * @param endtime the selection end time 
  86.      */  
  87.     public static void openInPraat(final String fileName, final long begintime,  
  88.         final long endtime) {  
  89.         new Thread() {  
  90.                 public void run() {  
  91.                     if (!checkScript()) {  
  92.                         JOptionPane.showMessageDialog(new JFrame(),  
  93.                             ElanLocale.getString(  
  94.                                 "PraatConnection.Message.NoScript"),  
  95.                             ElanLocale.getString("Message.Warning"),  
  96.                             JOptionPane.WARNING_MESSAGE);  
  97.                         LOG.warning("Praat script could not be created");  
  98.   
  99.                         return;  
  100.                     }  
  101.   
  102.                     if (System.getProperty("os.name").toLowerCase().indexOf("windows") > -1) {  
  103.                         openWindowsPraat(fileName, begintime, endtime);  
  104.                     } else {  
  105.                         openOtherPraat(fileName, begintime, endtime);  
  106.                     }  
  107.                 }  
  108.             }.start();  
  109.     }  
  110.   
  111.     /** 
  112.      * Asynchronously creates clip from a file with Praat and opens the new file as a Sound object. 
  113.      * 
  114.      * @param fileName the media file 
  115.      * @param begintime the selection begintime 
  116.      * @param endtime the selection end time 
  117.      */  
  118.     public static void clipWithPraat(final String fileName,  
  119.         final long begintime, final long endtime) {  
  120.         new Thread() {  
  121.                 public void run() {  
  122.                     if (!checkClipScript()) {  
  123.                         JOptionPane.showMessageDialog(new JFrame(),  
  124.                             ElanLocale.getString(  
  125.                                 "PraatConnection.Message.NoScript"),  
  126.                             ElanLocale.getString("Message.Warning"),  
  127.                             JOptionPane.WARNING_MESSAGE);  
  128.                         LOG.warning("Praat script could not be created");  
  129.   
  130.                         return;  
  131.                     }  
  132.   
  133.                     if (System.getProperty("os.name").toLowerCase().indexOf("windows") > -1) {  
  134.                         openWindowsPraat(fileName, begintime, endtime);  
  135.                     } else {  
  136.                         openOtherPraat(fileName, begintime, endtime);  
  137.                     }  
  138.                 }  
  139.             }.start();  
  140.     }  
  141.   
  142.     /** 
  143.      * For windows flavors some processing of path names should be done etc. 
  144.      * 
  145.      * @param fileName the media file 
  146.      * @param begintime the begintime of the selection 
  147.      * @param endtime the end time of the selection 
  148.      */  
  149.     private static void openWindowsPraat(String fileName, long begintime,  
  150.         long endtime) {  
  151.         if (fileName != null) {  
  152.             // Praat can handle files on samba shares too  
  153.             if (fileName.startsWith("///")) {  
  154.                 fileName = fileName.substring(3);  
  155.             }  
  156.   
  157.             fileName = fileName.replace('/', '\\');  
  158.   
  159.             // sendpraat has problems with filepaths containing spaces  
  160.             // single or double quotes don't seem to help  
  161.             // if the file name itself contains spaces there seems to be no solution  
  162.             if (fileName.indexOf(' ') > 0) {  
  163.                 fileName = spacelessWindowsPath(fileName);  
  164.             }  
  165.   
  166.             //System.out.println("file: " + fileName);  
  167.         } else {  
  168.             LOG.warning("Praat: media file is null");  
  169.   
  170.             return;  
  171.         }  
  172.   
  173.         // first make sure praat is running  
  174.         String praatExe = getPreference(PRAAT_APP);  
  175.   
  176.         if ((praatExe == null) || (praatExe.length() == 0)) {  
  177.             praatExe = "Praat.exe";  
  178.         }  
  179.   
  180.         String[] praatCom = new String[] { praatExe };  
  181.   
  182.         try {  
  183.             Runtime.getRuntime().exec(praatCom);  
  184.   
  185.             // give praat a moment to start  
  186.             try {  
  187.                 Thread.sleep(2000);  
  188.             } catch (InterruptedException ie) {  
  189.                 LOG.info(LogUtil.formatStackTrace(ie));  
  190.             }  
  191.   
  192.             //  
  193.         } catch (SecurityException se) {  
  194.             JOptionPane.showMessageDialog(new JFrame(),  
  195.                 ElanLocale.getString("PraatConnection.Message.Security"),  
  196.                 ElanLocale.getString("Message.Warning"),  
  197.                 JOptionPane.WARNING_MESSAGE);  
  198.             LOG.warning(LogUtil.formatStackTrace(se));  
  199.   
  200.             return;  
  201.         } catch (IOException ioe) {  
  202.             LOG.warning(LogUtil.formatStackTrace(ioe));  
  203.   
  204.             // not found, prompt  
  205.             String path = locatePraat();  
  206.   
  207.             if (path == null) {  
  208.                 return;  
  209.             } else {  
  210.                 // retry  
  211.                 openWindowsPraat(fileName, begintime, endtime);  
  212.   
  213.                 return;  
  214.             }  
  215.         }  
  216.   
  217.         // next execute sendpraat  
  218.         // (on windows we cannot use praatcon.exe for this task)  
  219.         String sendpraatExe = getPreference(SENDPRAAT_APP);  
  220.   
  221.         if ((sendpraatExe == null) || (sendpraatExe.length() == 0)) {  
  222.             sendpraatExe = "sendpraat.exe";  
  223.         }  
  224.   
  225.         String executeCom = "execute " + scriptFileName + " " + fileName + " " +  
  226.             String.valueOf(begintime) + " " + String.valueOf(endtime);  
  227.   
  228.         String[] sendpraatCom = new String[3];  
  229.         sendpraatCom[0] = sendpraatExe;  
  230.         sendpraatCom[1] = "Praat";  
  231.         sendpraatCom[2] = executeCom;  
  232.   
  233.         try {  
  234.             Runtime.getRuntime().exec(sendpraatCom);  
  235.         } catch (SecurityException se) {  
  236.             JOptionPane.showMessageDialog(new JFrame(),  
  237.                 ElanLocale.getString("PraatConnection.Message.Security"),  
  238.                 ElanLocale.getString("Message.Warning"),  
  239.                 JOptionPane.WARNING_MESSAGE);  
  240.             LOG.warning(LogUtil.formatStackTrace(se));  
  241.   
  242.             return;  
  243.         } catch (IOException ioe) {  
  244.             LOG.warning(LogUtil.formatStackTrace(ioe));  
  245.   
  246.             // not found, prompt  
  247.             String path = locateSendpraat();  
  248.   
  249.             if (path == null) {  
  250.                 return;  
  251.             } else {  
  252.                 // retry  
  253.                 openWindowsPraat(fileName, begintime, endtime);  
  254.             }  
  255.         }  
  256.     }  
  257.   
  258.     /** 
  259.      * Open Praat on non-windows systems; MacOS X, Unix, Linux. <br> 
  260.      * Note: on Mac OS X every call to runtime.exec to start Praat opens a new 
  261.      * instance of Praat: it is not detected that it is already open (this is 
  262.      * a result  of the fact that you can not use the Praat.app "folder" to 
  263.      * start Praat).  Maybe we should not try to start Praat but let the user 
  264.      * be responsible to  start Praat. 
  265.      * 
  266.      * @param fileName the media file 
  267.      * @param begintime the begintime of the selection 
  268.      * @param endtime the end time of the selection 
  269.      */  
  270.     private static void openOtherPraat(String fileName, long begintime,  
  271.         long endtime) {  
  272.         if (fileName != null) {  
  273.             // Praat can handle files on samba shares too  
  274.             // on Mac: when the url starts with 3 slashes, remove 2 slashes  
  275.             // TO DO: this needs testing on other platforms  
  276.             if (fileName.startsWith("///")) {  
  277.                 fileName = fileName.substring(2);  
  278.             }  
  279.         } else {  
  280.             LOG.warning("Praat: media file is null");  
  281.   
  282.             return;  
  283.         }  
  284.   
  285.         // first make sure praat is running  
  286.         String praatExe = getPreference(PRAAT_APP);  
  287.   
  288.         if ((praatExe == null) || (praatExe.length() == 0)) {  
  289.             praatExe = "praat";  
  290.         }  
  291.   
  292.         // check whether praat is running already; this is not a thread safe way to check  
  293.         isPraatRunning = false;  
  294.         checkUnixPraatProcess();  
  295.   
  296.         try {  
  297.             Thread.sleep(700);  
  298.         } catch (InterruptedException ie) {  
  299.         }  
  300.   
  301.         if (!isPraatRunning) {  
  302.             String[] praatCom = new String[] { praatExe };  
  303.   
  304.             try {  
  305.                 Runtime.getRuntime().exec(praatCom);  
  306.   
  307.                 // give praat a moment to start  
  308.                 try {  
  309.                     Thread.sleep(1500);  
  310.                 } catch (InterruptedException ie) {  
  311.                     LOG.info(LogUtil.formatStackTrace(ie));  
  312.                 }  
  313.             } catch (SecurityException se) {  
  314.                 JOptionPane.showMessageDialog(new JFrame(),  
  315.                     ElanLocale.getString("PraatConnection.Message.Security"),  
  316.                     ElanLocale.getString("Message.Warning"),  
  317.                     JOptionPane.WARNING_MESSAGE);  
  318.                 LOG.warning(LogUtil.formatStackTrace(se));  
  319.   
  320.                 return;  
  321.             } catch (IOException ioe) {  
  322.                 LOG.warning(LogUtil.formatStackTrace(ioe));  
  323.   
  324.                 // not found, prompt  
  325.                 String path = locatePraat();  
  326.   
  327.                 if (path == null) {  
  328.                     return;  
  329.                 } else {  
  330.                     // retry  
  331.                     openOtherPraat(fileName, begintime, endtime);  
  332.   
  333.                     return;  
  334.                 }  
  335.             }  
  336.         }  
  337.   
  338.         // next execute sendpraat  
  339.         String sendpraatExe = getPreference(SENDPRAAT_APP);  
  340.   
  341.         if ((sendpraatExe == null) || (sendpraatExe.length() == 0)) {  
  342.             sendpraatExe = "sendpraat";  
  343.         }  
  344.   
  345.         // test this om other platforms...  
  346.         String executeCom = "execute " + scriptFileName + " " + fileName + " " +  
  347.             String.valueOf(begintime) + " " + String.valueOf(endtime);  
  348.         String[] sendpraatCom = new String[3];  
  349.         sendpraatCom[0] = sendpraatExe;  
  350.         sendpraatCom[1] = "praat";  
  351.         sendpraatCom[2] = executeCom;  
  352.   
  353.         try {  
  354.             Runtime.getRuntime().exec(sendpraatCom);  
  355.         } catch (SecurityException se) {  
  356.             JOptionPane.showMessageDialog(new JFrame(),  
  357.                 ElanLocale.getString("PraatConnection.Message.Security"),  
  358.                 ElanLocale.getString("Message.Warning"),  
  359.                 JOptionPane.WARNING_MESSAGE);  
  360.             LOG.warning(LogUtil.formatStackTrace(se));  
  361.   
  362.             return;  
  363.         } catch (IOException ioe) {  
  364.             LOG.warning(LogUtil.formatStackTrace(ioe));  
  365.   
  366.             // not found, prompt  
  367.             String path = locateSendpraat();  
  368.   
  369.             if (path == null) {  
  370.                 return;  
  371.             } else {  
  372.                 // retry  
  373.                 openOtherPraat(fileName, begintime, endtime);  
  374.             }  
  375.         }  
  376.     }  
  377.   
  378.     /** 
  379.      * Checks whether or not the praatscript file already exists.  When not it 
  380.      * is created in the elan directory in the user's home directory. 
  381.      * 
  382.      * @return true if the file already existed or could be created, false 
  383.      *         otherwise 
  384.      */  
  385.     private static boolean checkScript() {  
  386.         if (!checkHome()) {  
  387.             return false;  
  388.         }  
  389.   
  390.         scriptFileName = Constants.ELAN_DATA_DIR + Constants.FILESEPARATOR +  
  391.             PRAAT_SCRIPT;  
  392.   
  393.         if (System.getProperty("os.name").toLowerCase().indexOf("windows") > -1) {  
  394.             // Praat on Windows does not like spaces in the path   
  395.             if (scriptFileName.indexOf(' ') > -1) {  
  396.                 String dir = System.getProperty("java.io.tmpdir");  
  397.   
  398.                 if (dir != null) {  
  399.                     scriptFileName = dir + Constants.FILESEPARATOR +  
  400.                         PRAAT_SCRIPT;  
  401.                 }  
  402.   
  403.                 // or  
  404.                 // scriptFileName = spacelessWindowsPath(scriptFileName);  
  405.             }  
  406.         }  
  407.   
  408.         File file = new File(scriptFileName);  
  409.   
  410.         if (file.exists()) {  
  411.             return true;  
  412.         } else {  
  413.             // first try to copy the file from the .jar  
  414.             if (copyScriptFromJar(file,  
  415.                         "/mpi/eudico/client/annotator/resources/openpraat.praat")) {  
  416.                 return true;  
  417.             } else {  
  418.                 // fallback: create the file programmatically  
  419.                 return createScriptFile(file);  
  420.             }  
  421.         }  
  422.     }  
  423.   
  424.     /** 
  425.      * Checks whether or not the praat clip script file already exists.  When not it 
  426.      * is created in the elan directory in the user's home directory. 
  427.      * 
  428.      * @return true if the file already existed or could be created, false 
  429.      *         otherwise 
  430.      */  
  431.     private static boolean checkClipScript() {  
  432.         if (!checkHome()) {  
  433.             return false;  
  434.         }  
  435.   
  436.         scriptFileName = Constants.ELAN_DATA_DIR + Constants.FILESEPARATOR +  
  437.             PRAAT_CLIP_SCRIPT;  
  438.   
  439.         if (System.getProperty("os.name").toLowerCase().indexOf("windows") > -1) {  
  440.             // Praat on Windows does not like spaces in the path   
  441.             if (scriptFileName.indexOf(' ') > -1) {  
  442.                 String dir = System.getProperty("java.io.tmpdir");  
  443.   
  444.                 if (dir != null) {  
  445.                     scriptFileName = dir + Constants.FILESEPARATOR +  
  446.                         PRAAT_SCRIPT;  
  447.                 }  
  448.   
  449.                 // or  
  450.                 // scriptFileName = spacelessWindowsPath(scriptFileName);  
  451.             }  
  452.         }  
  453.   
  454.         File file = new File(scriptFileName);  
  455.   
  456.         if (file.exists()) {  
  457.             return true;  
  458.         } else {  
  459.             // first try to copy the file from the .jar  
  460.             if (copyScriptFromJar(file,  
  461.                         "/mpi/eudico/client/annotator/resources/createsoundclip.praat")) {  
  462.                 return true;  
  463.             } else {  
  464.                 return false;  
  465.             }  
  466.         }  
  467.     }  
  468.   
  469.     /** 
  470.      * Checks whether the elan home dir exists. When not create it. 
  471.      * 
  472.      * @return true if the dir already existed or could be created, false 
  473.      *         otherwise 
  474.      */  
  475.     private static boolean checkHome() {  
  476.         File dataDir = new File(Constants.ELAN_DATA_DIR);  
  477.   
  478.         if (!dataDir.exists()) {  
  479.             try {  
  480.                 dataDir.mkdir();  
  481.             } catch (Exception e) {  
  482.                 LOG.warning(LogUtil.formatStackTrace(e));  
  483.   
  484.                 return false;  
  485.             }  
  486.         }  
  487.   
  488.         return true;  
  489.     }  
  490.   
  491.     /** 
  492.      * Tries to copy the script file from the jar. 
  493.      * 
  494.      * @param copyFile the copy of the script 
  495.      * @param resource the fully qualified name of the resource 
  496.      * 
  497.      * @return true if the operation succeeded, false otherwise 
  498.      */  
  499.     private static boolean copyScriptFromJar(File copyFile, String resource) {  
  500.         BufferedInputStream inStream;  
  501.         FileOutputStream out;  
  502.   
  503.         try {  
  504.             copyFile.createNewFile();  
  505.   
  506.             URL scriptSrc = PraatConnection.class.getResource(resource);  
  507.   
  508.             if (scriptSrc == null) {  
  509.                 LOG.warning("No script source file found");  
  510.   
  511.                 return false;  
  512.             }  
  513.   
  514.             inStream = new BufferedInputStream(scriptSrc.openStream());  
  515.   
  516.             byte[] buf = new byte[512];  
  517.             int n;  
  518.             out = new FileOutputStream(copyFile);  
  519.   
  520.             while ((n = inStream.read(buf)) > -1) {  
  521.                 out.write(buf, 0, n);  
  522.             }  
  523.   
  524.             out.flush();  
  525.             out.close();  
  526.             inStream.close();  
  527.   
  528.             return true;  
  529.         } catch (IOException ioe) {  
  530.             LOG.warning(LogUtil.formatStackTrace(ioe));  
  531.   
  532.             return false;  
  533.         }  
  534.     }  
  535.   
  536.     /** 
  537.      * Writes the script file to the elan home dir. 
  538.      * 
  539.      * @param scriptFile the file to write to 
  540.      * 
  541.      * @return true if everything went allright, false otherwise 
  542.      */  
  543.     private static boolean createScriptFile(File scriptFile) {  
  544.         try {  
  545.             if (!scriptFile.exists()) {  
  546.                 scriptFile.createNewFile();  
  547.             }  
  548.   
  549.             String contents = createScriptContents();  
  550.             FileWriter writer = new FileWriter(scriptFile);  
  551.             writer.write(contents);  
  552.             writer.close();  
  553.         } catch (IOException ioe) {  
  554.             LOG.warning(LogUtil.formatStackTrace(ioe));  
  555.   
  556.             return false;  
  557.         }  
  558.   
  559.         return true;  
  560.     }  
  561.   
  562.     /** 
  563.      * Create the script. Temp. could be copied from the jar... 
  564.      * 
  565.      * @return the scriptcontents 
  566.      */  
  567.     private static String createScriptContents() {  
  568.         StringBuffer scriptBuffer = new StringBuffer();  
  569.         scriptBuffer.append("form Segment info\n");  
  570.         scriptBuffer.append("\ttext Filename \"\"\n");  
  571.         scriptBuffer.append("\tpositive Start 0\n");  
  572.         scriptBuffer.append("\tpositive End 10\n");  
  573.         scriptBuffer.append("endform\n");  
  574.         scriptBuffer.append("len = length( filename$)\n");  
  575.         scriptBuffer.append("dot = rindex (filename$, \".\")\n");  
  576.         scriptBuffer.append("slash = rindex (filename$, \"/\")\n");  
  577.         scriptBuffer.append("if slash == 0\n");  
  578.         scriptBuffer.append("\tslash = rindex (filename$, \"\\\")\n");  
  579.         scriptBuffer.append("endif\n");  
  580.         scriptBuffer.append(  
  581.             "stem$ = mid$ (filename$ , slash+1, dot - slash - 1 )\n");  
  582.   
  583.         //scriptBuffer.append("stem$ = replace$ (stem$, \".\", \"_\", 0)");   
  584.         scriptBuffer.append("Open long sound file... 'filename$'\n");  
  585.         scriptBuffer.append("s = start / 1000\n");  
  586.         scriptBuffer.append("en = end / 1000\n");  
  587.         scriptBuffer.append("View\n");  
  588.         scriptBuffer.append("editor LongSound 'stem$'\n");  
  589.         scriptBuffer.append("\tSelect... 's' 'en'\n");  
  590.         scriptBuffer.append("\tZoom to selection\n");  
  591.         scriptBuffer.append("endeditor");  
  592.   
  593.         return scriptBuffer.toString();  
  594.     }  
  595.   
  596.     /** 
  597.      * Promt the user for the location of the Praat executable. 
  598.      * 
  599.      * @return the path to the Praat executable 
  600.      */  
  601.     private static String locatePraat() {  
  602.         String praatPath = null;  
  603.         JFileChooser chooser = new JFileChooser();  
  604.         chooser.setDialogTitle(ElanLocale.getString(  
  605.                 "PraatConnection.LocateDialog.Title1"));  
  606.         chooser.setApproveButtonText(ElanLocale.getString(  
  607.                 "PraatConnection.LocateDialog.Select"));  
  608.   
  609.         int option = chooser.showOpenDialog(new JFrame());  
  610.   
  611.         if (option == JFileChooser.CANCEL_OPTION) {  
  612.             // cannot remove a preference yet  
  613.             setPreference(PRAAT_APP, "");  
  614.         } else {  
  615.             File path = chooser.getSelectedFile();  
  616.             praatPath = path.getAbsolutePath();  
  617.   
  618.             // Mac specific addition  
  619.             if (path.isDirectory() && praatPath.endsWith(".app")) {  
  620.                 // append path to the actual executable  
  621.                 praatPath += "/Contents/MacOS/Praat";  
  622.             }  
  623.   
  624.             // trust the user, cannot possibly check the value  
  625.             setPreference(PRAAT_APP, praatPath);  
  626.         }  
  627.   
  628.         return praatPath;  
  629.     }  
  630.   
  631.     /** 
  632.      * Promt the user for the location of the sendpraat executable. 
  633.      * 
  634.      * @return the path to the sendpraat executable 
  635.      */  
  636.     private static String locateSendpraat() {  
  637.         String sendpraatPath = null;  
  638.         JFileChooser chooser = new JFileChooser();  
  639.         chooser.setDialogTitle(ElanLocale.getString(  
  640.                 "PraatConnection.LocateDialog.Title2"));  
  641.         chooser.setApproveButtonText(ElanLocale.getString(  
  642.                 "PraatConnection.LocateDialog.Select"));  
  643.   
  644.         int option = chooser.showOpenDialog(new JFrame());  
  645.   
  646.         if (option == JFileChooser.CANCEL_OPTION) {  
  647.             // cannot remove a preference yet  
  648.             setPreference(SENDPRAAT_APP, "");  
  649.         } else {  
  650.             File path = chooser.getSelectedFile();  
  651.             sendpraatPath = path.getAbsolutePath();  
  652.   
  653.             // trust the user, cannot possibly check the value  
  654.             setPreference(SENDPRAAT_APP, sendpraatPath);  
  655.         }  
  656.   
  657.         return sendpraatPath;  
  658.     }  
  659.   
  660.     /** 
  661.      * Returns the pref for the specified key like the Praat and sendpraat 
  662.      * executable paths from the praat pref file. 
  663.      * 
  664.      * @param key the key for the pref 
  665.      * 
  666.      * @return the pref 
  667.      */  
  668.     private static String getPreference(String key) {  
  669.         if (key == null) {  
  670.             return null;  
  671.         }  
  672.   
  673.         if (preferences == null) {  
  674.             preferences = loadPreferences();  
  675.         }  
  676.   
  677.         return (String) preferences.get(key);  
  678.     }  
  679.   
  680.     /** 
  681.      * Loads the praat preferences from the praat pref file. 
  682.      * 
  683.      * @return a hashtable with the pref mappings 
  684.      */  
  685.     private static Hashtable loadPreferences() {  
  686.         Hashtable hashtable = new Hashtable();  
  687.         ;  
  688.   
  689.         File inFile = new File(PRAAT_PREFS_FILE);  
  690.   
  691.         try {  
  692.             //if (inFile.exists()) {  
  693.             FileInputStream inStream = new FileInputStream(inFile);  
  694.             ObjectInputStream objectIn = new ObjectInputStream(inStream);  
  695.             hashtable = (Hashtable) objectIn.readObject();  
  696.             objectIn.close();  
  697.             inStream.close();  
  698.   
  699.             //}  
  700.         } catch (FileNotFoundException fnfe) {  
  701.             LOG.warning("Could not load Praat preferences");  
  702.             LOG.warning(LogUtil.formatStackTrace(fnfe));  
  703.         } catch (IOException ioe) {  
  704.             LOG.warning("Could not load Praat preferences");  
  705.             LOG.warning(LogUtil.formatStackTrace(ioe));  
  706.         } catch (ClassNotFoundException cnfe) {  
  707.             LOG.warning("Could not load Praat preferences");  
  708.             LOG.warning(LogUtil.formatStackTrace(cnfe));  
  709.         }  
  710.   
  711.         return hashtable;  
  712.     }  
  713.   
  714.     /** 
  715.      * Sets the specified pref to the new value. 
  716.      * 
  717.      * @param key the pref key 
  718.      * @param value the pref value 
  719.      */  
  720.     private static void setPreference(String key, String value) {  
  721.         if ((key == null) || (value == null)) {  
  722.             return;  
  723.         }  
  724.   
  725.         if (preferences == null) {  
  726.             preferences = new Hashtable();  
  727.         }  
  728.   
  729.         preferences.put(key, value);  
  730.   
  731.         savePreferences();  
  732.     }  
  733.   
  734.     /** 
  735.      * Wrties the praat preferences file to disc. 
  736.      */  
  737.     private static void savePreferences() {  
  738.         if (preferences == null) {  
  739.             return;  
  740.         }  
  741.   
  742.         try {  
  743.             FileOutputStream outStream = new FileOutputStream(PRAAT_PREFS_FILE);  
  744.             ObjectOutputStream objectOut = new ObjectOutputStream(outStream);  
  745.             objectOut.writeObject(preferences);  
  746.             objectOut.close();  
  747.             outStream.close();  
  748.         } catch (FileNotFoundException fnfe) {  
  749.             LOG.warning("Could not save Praat preferences");  
  750.             LOG.warning(LogUtil.formatStackTrace(fnfe));  
  751.         } catch (IOException ioe) {  
  752.             LOG.warning("Could not save Praat preferences");  
  753.             LOG.warning(LogUtil.formatStackTrace(ioe));  
  754.         }  
  755.     }  
  756.   
  757.     /** 
  758.      * Tries to check whether or not Praat is already running using the  Unix 
  759.      * utility <code>top</code>. 
  760.      */  
  761.     private static void checkUnixPraatProcess() {  
  762.         try {  
  763.             final Process p = Runtime.getRuntime().exec(new String[] {  
  764.                         "top", "-l1"  
  765.                     });  
  766.   
  767.             new Thread(new Runnable() {  
  768.                     public void run() {  
  769.                         try {  
  770.                             InputStreamReader isr = new InputStreamReader(p.getInputStream());  
  771.                             BufferedReader br = new BufferedReader(isr);  
  772.                             String line = null;  
  773.   
  774.                             while ((line = br.readLine()) != null) {  
  775.                                 if (line.indexOf(" Praat ") > 0) {  
  776.                                     br.close();  
  777.                                     isPraatRunning = true;  
  778.   
  779.                                     break;  
  780.                                 }  
  781.                             }  
  782.                         } catch (IOException ioe) {  
  783.                             LOG.warning(LogUtil.formatStackTrace(ioe));  
  784.                         }  
  785.                     }  
  786.                 }).start();  
  787.         } catch (SecurityException sex) {  
  788.             LOG.warning(LogUtil.formatStackTrace(sex));  
  789.         } catch (IOException ioe) {  
  790.             LOG.warning(LogUtil.formatStackTrace(ioe));  
  791.         }  
  792.     }  
  793.   
  794.     /** 
  795.      * Converts a file path containing spaces in directory names to a path with 
  796.      * "DIRECT~x" elements. The filename itself will not be converted. 
  797.      * 
  798.      * @param fileName the file path with spaces 
  799.      * 
  800.      * @return a converted path 
  801.      */  
  802.     private static String spacelessWindowsPath(String fileName) {  
  803.         if (fileName == null) {  
  804.             return null;  
  805.         }  
  806.   
  807.         //String path = fileName;  
  808.         int firstSpace = fileName.indexOf(' ');  
  809.   
  810.         if (firstSpace  0) {  
  811.             return fileName;  
  812.         }  
  813.   
  814.         int prevSep = fileName.lastIndexOf(File.separator, firstSpace);  
  815.         int nextSep = fileName.indexOf(File.separator, firstSpace);  
  816.         String fileOrDirName = null;  
  817.   
  818.         if (nextSep > 0) {  
  819.             fileOrDirName = fileName.substring(prevSep + 1, nextSep);  
  820.         } else {  
  821.             //fileOrDirName = fileName.substring(prevSep + 1);  
  822.             return fileName;  
  823.         }  
  824.   
  825.         // if an tilde already has been added, prevSep + 1 does not include the file separator ??   
  826.         try {  
  827.             File dir = new File(fileName.substring(0, prevSep + 1));  
  828.   
  829.             if (!dir.isDirectory()) {  
  830.                 return fileName;  
  831.             }  
  832.   
  833.             String start = fileOrDirName.substring(0, fileOrDirName.indexOf(' '));  
  834.             String[] fNames = dir.list();  
  835.   
  836.             int sufNum = 0;  
  837.   
  838.             for (int i = 0; i  fNames.length; i++) {  
  839.                 if (fNames[i].startsWith(fileOrDirName)) {  
  840.                     sufNum++;  
  841.   
  842.                     if (fNames[i].equals(fileOrDirName)) {  
  843.                         break;  
  844.                     }  
  845.                 }  
  846.             }  
  847.   
  848.             StringBuffer pathBuf = new StringBuffer(dir.getPath());  
  849.   
  850.             // see comment above  
  851.             if (pathBuf.charAt(pathBuf.length() - 1) != File.separatorChar) {  
  852.                 pathBuf.append(File.separator);  
  853.             }  
  854.   
  855.             if (start.length() = 6) {  
  856.                 String tmp = fileOrDirName.replaceAll(" ", "");  
  857.   
  858.                 if (tmp.length() = 6) {  
  859.                     pathBuf.append(tmp.toUpperCase()).append("~").append(sufNum);  
  860.                 } else {  
  861.                     pathBuf.append(tmp.substring(0, 6).toUpperCase()).append("~")  
  862.                            .append(sufNum);  
  863.                 }  
  864.             } else {  
  865.                 pathBuf.append(start.substring(0, 6).toUpperCase()).append("~")  
  866.                        .append(sufNum);  
  867.             }  
  868.   
  869.             if (nextSep > 0) {  
  870.                 pathBuf.append(fileName.substring(nextSep));  
  871.             }  
  872.   
  873.             if (pathBuf.indexOf(" ")  0) {  
  874.                 return pathBuf.toString();  
  875.             } else {  
  876.                 return spacelessWindowsPath(pathBuf.toString());  
  877.             }  
  878.         } catch (Exception ex) {  
  879.             LOG.warning("Invalid directory: " + ex.getMessage());  
  880.   
  881.             return fileName;  
  882.         }  
  883.     }  
  884. }