import java.io.*; import javax.sound.sampled.*; /** * The UltrasoundListener class can be used to determine if a signal of * a particular frequency is present. After initialization a user of this * class only needs to specify the amount of time the class should aquire * the microphone to listen to the signal, and then analyze the data. * Parts of the aduio capturing used in this program have been based upon examples * available at http://www.developer.com/java/other/article.php/2105421 The * examples at this page specifically deal with capturing data from a microphone * to be used for playback. * * @author Chris Palistrant, Tony Offer * @version 0.0, May 2004 */ public class UltrasoundListener{ boolean debugger = false; boolean stopCapture = false; boolean intense = false; UltrasoundDetector usd; ByteArrayOutputStream byteArrayOutputStream; AudioFormat audioFormat; AudioInputStream audioInputStream; SourceDataLine sourceDataLine; TargetDataLine targetDataLine; /** * Default Constructor. By default we have a sampling rate of 44.1kHz. * Additionally we are searching for a a frequency in the Ultrasonic * range specifically 21kHz. */ public UltrasoundListener(){ this(44100.0F, 21000.0F,420); } /** * Constructor. This constructor allows a user to sepcify the specific * parameters to analyze. * @param sampleRate is the sampling rate of the signal to be analyzed * @param targetFreq is the frequency that Goertzel will look for. * @param n is the block size to use with the underlying signal analysis process. */ public UltrasoundListener(float sampleRate, float targetFreq, int n){ usd = new UltrasoundDetector(sampleRate, targetFreq, n); } /** * Initializes the class. Should be called after constructing an object. */ public void init(){ usd.g.DEBUG = debugger; usd.signalAnalysisInit(); } /** * This method creates and returns an AudioFormat object for a given set * of format parameters. If these parameters don't work well for * you, try some of the other allowable parameter values, which * are shown in comments following the declarations. * * The frame size is equal to number of channels * bytes per sample * Our frame size is equal to 4 because 2 bytes per sample */ private AudioFormat getAudioFormat(){ float sampleRate = 44100.0F; int sampleSizeInBits = 16; int channels = 1; //mono boolean signed = true; boolean bigEndian = false; //x86 uses little/ javac uses big //AudioFormat with a linear PCM encoding and the given parameters. return new AudioFormat(sampleRate,sampleSizeInBits,channels,signed,bigEndian); } /** * Listens for a period of time. The parameter given is the duration to listen. * * @param msec should be a long in millisec units */ public void listen(long msec){ long dummy = 0; //Set everything up for capture audioFormat = getAudioFormat(); //Start recording if(debugger) System.out.println("Recording..."); try { if(debugger) System.out.println("Making Data Line..."); DataLine.Info dataLineInfo = new DataLine.Info(TargetDataLine.class, audioFormat); targetDataLine = (TargetDataLine)AudioSystem.getLine(dataLineInfo); if(debugger) System.out.println("Getting Line..."); targetDataLine.open(audioFormat); if(debugger) System.out.println("Starting..."); targetDataLine.start(); //Create a thread to capture the microphone data and start it //running. It will run until the time runs out Thread captureThread = new Thread(new CaptureThread()); captureThread.start(); if(debugger) System.out.println("Started"); } catch (Exception e) { System.out.println("Exiting prematurely:" + e); System.exit(0); } long t1 = msec + System.currentTimeMillis(); if(debugger) System.out.println("Entering count down."); while(t1 > System.currentTimeMillis()) { dummy++; } if(debugger) System.out.println("Preparing to stop data line"); targetDataLine.stop(); if(debugger) System.out.println("Closing data line"); targetDataLine.close(); } /** * Called to perform UltraSoundDetector functions * * @return true if signal is detected, false otherwise. */ public boolean analyze(){ //Get everything set up for analysis. //Get the previously-saved data into a byte array object. byte audioData[] = byteArrayOutputStream.toByteArray(); java.lang.Byte highOrder, lowOrder = null; int sample = 0; double[] samples = new double[audioData.length/2 + 1]; if(debugger) System.out.println("samples length is " + samples.length); for (int i=0, j = 0; i < audioData.length-1; i += 2, j++) { lowOrder = new java.lang.Byte(audioData[i]); highOrder = new java.lang.Byte(audioData[i+1]); if(debugger && intense){ System.out.println("H: " + highOrder.intValue() + " L: " + lowOrder.intValue()); System.out.println("Double sample is: " + sample); } sample = (highOrder.intValue() << 8) | (0xFF & lowOrder.intValue()); samples[j] = (double)sample; } if(usd.loadSignal(samples)){ if(debugger) System.out.println("Signal appears to be loaded. Beginning test..."); return usd.testSignal(); } else{ System.out.println("Unable to load signal: Possibly no recording"); return false; } } /** * Used for testing to ensure the audio file was captured. */ private void testPlayCapture(){ try{ //Get everything set up for playback. //Get the previously-saved data into a byte array object. byte audioData[] = byteArrayOutputStream.toByteArray(); // Write the bytes directly to a file java.lang.Byte highOrder, lowOrder = null; int sample = 0; FileWriter fileWriter = new FileWriter(new File("sound.raw")); for (int i = 0; i < audioData.length-1; i += 2) { lowOrder = new java.lang.Byte(audioData[i]); highOrder = new java.lang.Byte(audioData[i+1]); sample = (highOrder.intValue() << 8) | (0xFF & lowOrder.intValue()); String theNum = new Integer(sample).toString(); fileWriter.write(theNum + "\n"); } //Get an input stream on the byte array containing the data InputStream byteArrayInputStream = new ByteArrayInputStream(audioData); AudioFormat audioFormat = getAudioFormat(); audioInputStream = new AudioInputStream(byteArrayInputStream,audioFormat, audioData.length/audioFormat.getFrameSize()); DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class,audioFormat); sourceDataLine = (SourceDataLine)AudioSystem.getLine(dataLineInfo); sourceDataLine.open(audioFormat); sourceDataLine.start(); //Create a thread to play back the data and start it // running. It will run until all the data has been played back. Thread playThread = new Thread(new PlayThread()); playThread.start(); } catch (Exception e) { System.out.println(e); System.exit(0); } } /** * This program can be given either no options or "-d" or "-dp" or "-di" * * @param args can be one of the following different options: "-d" which is normal * and simplest debugging mode. It gives a play by play narrative of whats happening. * Option "-dp" will do the same thing only play the audio file captured. And "-di" is * only for those with a string stomach, since it jets each out the captured samples * to the terminal. */ public static void main(String[] args){ boolean debug = false; boolean play = false; UltrasoundListener usl = new UltrasoundListener();; if(args.length > 0){ if(args[0].equals("-d")){ debug = true; usl.debugger = true; } else if (args[0].equals("-dp")){ debug = true; play = true; usl.debugger = true; } else if (args[0].equals("-di")){ debug = true; usl.intense = true; usl.debugger = true; } } //init, listen, analyze usl.init(); if(debug) System.out.println("Starting to listen:"); usl.listen(5000); if(debug) System.out.println("Starting to analyze:"); boolean temp = usl.analyze(); if(debug) System.out.println("The analysis returned: " + temp); if(play){ System.out.println("Done Listening: preparing to Play Data"); usl.testPlayCapture(); System.out.println("Done Playing."); } try { System.in.read(); System.exit(0); } catch (IOException e) { e.printStackTrace(); System.exit(0); } } //======================================= //Inner class used to capture data from microphone class CaptureThread extends Thread{ //An arbitrary-size temporary holding buffer //byte tempBuffer[] = new byte[100000]; byte tempBuffer[] = new byte[2000]; public void run(){ byteArrayOutputStream = new ByteArrayOutputStream(); stopCapture = false; try{//Loop until stopCapture is set by another thread that // services the Stop button. while(!stopCapture){ //Read data from the internal buffer of the data line. int cnt = targetDataLine.read(tempBuffer,0,tempBuffer.length); if(cnt > 0){ //Save data in output stream object. byteArrayOutputStream.write(tempBuffer, 0, cnt); }//end if }//end while byteArrayOutputStream.close(); }catch (Exception e) { System.out.println(e); System.exit(0); } }//end run }//end inner class CaptureThread //===================================// //Inner class to play back the data that was saved. class PlayThread extends Thread{ byte tempBuffer[] = new byte[10000]; public void run(){ try{ int cnt; //Keep looping until the input read method returns -1 for empty stream. while((cnt = audioInputStream.read(tempBuffer, 0,tempBuffer.length)) != -1){ if(debugger) System.out.println("# Bytes read "+ cnt + " for thread " + this.toString()); if(cnt > 0){ //Write data to the internal buffer of the data line // where it will be delivered to the speaker. sourceDataLine.write(tempBuffer, 0, cnt); }//end if }//end while //Block and wait for internal buffer of the data line to empty. sourceDataLine.drain(); sourceDataLine.close(); }catch (Exception e) { System.out.println(e); System.exit(0); } }//end run }//end inner class PlayThread //===================================// }