Advertisement
Guest User

Untitled

a guest
Aug 11th, 2012
1,532
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 13.84 KB | None | 0 0
  1. /*
  2.     Save code below as Recognizer.java
  3. */
  4.  
  5. package com.example;
  6. import com.example.Recognizer;
  7.  
  8. class Recognizer {
  9.     public function Recognizer() {
  10.         this.exs = new ExecutorService;
  11.         this.recorder = new Recorder(exs);
  12.     }
  13. }
  14.  
  15. /*
  16.     Save code below as Recorder.java
  17. */
  18.  
  19. package com.example;
  20.  
  21. import java.io.File;
  22. import java.io.FileInputStream;
  23. import java.io.IOException;
  24. import java.nio.ByteBuffer;
  25. import java.nio.ByteOrder;
  26. import java.nio.ShortBuffer;
  27. import java.util.LinkedList;
  28. import java.util.concurrent.ExecutorService;
  29. import java.util.concurrent.Executors;
  30. import javaFlacEncoder.FLAC_FileEncoder;
  31.  
  32. import javax.sound.sampled.AudioFileFormat;
  33. import javax.sound.sampled.AudioFormat;
  34. import javax.sound.sampled.AudioInputStream;
  35. import javax.sound.sampled.AudioSystem;
  36. import javax.sound.sampled.DataLine;
  37. import javax.sound.sampled.LineUnavailableException;
  38. import javax.sound.sampled.TargetDataLine;
  39.  
  40. public class Recorder {
  41.  
  42.     /**
  43.      * Microphone is a nested Class for executing the recording
  44.      */
  45.  
  46.     public class Microphone extends Thread
  47.     {
  48.         private TargetDataLine          m_line;
  49.         private AudioFileFormat.Type    m_targetType;
  50.         private AudioInputStream        m_audioInputStream;
  51.         private File                    m_outputFile;
  52.  
  53.         public Microphone(TargetDataLine line, AudioFileFormat.Type targetType, File file)
  54.         {
  55.             m_line = line;
  56.             m_audioInputStream = new AudioInputStream(line);
  57.             m_targetType = targetType;
  58.             m_outputFile = file;
  59.         }
  60.  
  61.         //start recording
  62.         public void start()
  63.         {
  64.             m_line.start();
  65.             super.start();
  66.         }
  67.  
  68.         //stop recording
  69.         public void stopRecording()
  70.         {
  71.             m_line.stop();
  72.             m_line.close();
  73.         }
  74.  
  75.         public void run()
  76.         {
  77.             m_line.start();
  78.             try {
  79.                 AudioSystem.write(m_audioInputStream, m_targetType, m_outputFile);
  80.             }
  81.             catch (IOException e)
  82.             {
  83.                 e.printStackTrace();
  84.             }
  85.         }
  86.     }
  87.  
  88.     private String strFilename;
  89.     private File outputFile;
  90.     private File croppedWaveFile;
  91.     private AudioFormat audioFormat;
  92.     private TargetDataLine primaryLine;
  93.     private TargetDataLine  secondaryLine;
  94.     private AudioFileFormat.Type targetType;
  95.     private Microphone  mic1;
  96.     private double maxRMS;
  97.     private double staticAverage;
  98.     private double signalAverage;
  99.     private long speechDetectionTime;
  100.     private boolean speechDetected;
  101.  
  102.     private final double NOISE_FACTOR =         4.00;
  103.     private final long BYTES_PER_MILLISECOND =  16*4;
  104.     private final long WAVE_HEADER =            44;
  105.     private final double SILENCE_FACTOR =       0.20;
  106.     private final long NOSPEECH_TIMEOUT =       10000;
  107.     private final long LONGSPEECH_TIMEOUT =     10000;
  108.     private final ExecutorService exs;
  109.     private final int SAMPLE_RATE =             40;
  110.     private final long SMOOTHENING_BUFFER =     200;
  111.     private final int AUTOSTOP_RATE =           40;
  112.  
  113.  
  114.     /** Constructor, only requires the execution service for threading*/
  115.     public Recorder(ExecutorService ExServ){
  116.  
  117.         // Assign Executor Service to exs
  118.         this.exs = ExServ;
  119.         this.speechDetected = false;
  120.         this.strFilename = "infile.wav";
  121.         this.outputFile = new File(strFilename);
  122.  
  123.         /*  Create Audioformat
  124.             Frame Rate 16Khz from the Sample Rate of 16Khz
  125.             2 Channels and 16 bit per channel
  126.          */
  127.         this.audioFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 16000.0F, 16, 2, 4, 16000.0F, false);
  128.        
  129.         /*Get a 2 datalines from the Computer microphone with given Audio Format*/
  130.         DataLine.Info   info = new DataLine.Info(TargetDataLine.class, audioFormat);
  131.         this.primaryLine = null;
  132.  
  133.         DataLine.Info   info2 = new DataLine.Info(TargetDataLine.class, audioFormat);
  134.         this.secondaryLine = null;
  135.  
  136.         /* Try to open a data line with defined AudioFormat*/
  137.         try{
  138.             primaryLine = (TargetDataLine) AudioSystem.getLine(info);
  139.             primaryLine.open(audioFormat);
  140.             primaryLine.start();
  141.             secondaryLine = (TargetDataLine) AudioSystem.getLine(info2);
  142.             secondaryLine.open(audioFormat);
  143.             secondaryLine.start();
  144.         }
  145.         catch (LineUnavailableException luEx){
  146.             System.out.println("unable to get a recording line");
  147.             luEx.printStackTrace();
  148.             System.exit(1);
  149.         }
  150.  
  151.         /*Save data as format WAVE*/
  152.         this.targetType = AudioFileFormat.Type.WAVE;
  153.  
  154.  
  155.         /*Create a Recorder2 to Get Input Data on mic1*/
  156.         this.mic1 = new Microphone(primaryLine, targetType, outputFile);
  157.  
  158.     }
  159.  
  160.     /** isActive(); calling this method will block execution until speech is heard or the microphone has timed out
  161.      *  will return true once voice activity is measured otherwise false*/
  162.     public boolean isActive() throws InterruptedException, IOException{
  163.  
  164.         /* Get the bytes per second */
  165.         int bytesPerSec =               audioFormat.getSampleSizeInBits() * (int) audioFormat.getSampleRate();
  166.  
  167.         /* Set the sampling frequency [assuming samples at this rate ...]*/
  168.         int sampleDataBufferSize =      bytesPerSec / this.SAMPLE_RATE;
  169.  
  170.         /* Create a byte array of appropriate sample size*/
  171.         byte[] sampleDataByteArray =    new byte[sampleDataBufferSize];
  172.  
  173.         /* Start the microphone and the secondaryLine for sampling*/
  174.         //exs.submit(mic1);
  175.         mic1.start();
  176.         secondaryLine.start();
  177.  
  178.         /* Intializing for scope*/
  179.         int sampleCount =               0;
  180.         long startTime =                System.currentTimeMillis();
  181.         LinkedList<Double> rmsDeque =   new LinkedList<Double>();
  182.         double total =                  0;
  183.  
  184.         /* Indicate that recording has started*/
  185.         System.out.println("Listening");
  186.  
  187.         /* While we are silent, i.e no speech detected, we remain in this loop*/
  188.         while (isSilent(sampleCount++,startTime)){
  189.  
  190.             /* Blocking Read Assignment : will fill sampleDataByteArray */
  191.             secondaryLine.read(sampleDataByteArray, 0, sampleDataBufferSize);
  192.            
  193.             /* Convert the array to floating point values between zero and one*/
  194.             float[] fArray =    Recorder.getFloatArray(sampleDataByteArray);
  195.  
  196.             /* Determine the root mean square*/
  197.             Double rms =        Recorder.getRMS(fArray);
  198.  
  199.             /* Push the rms to the back of the deque*/
  200.             rmsDeque.push(rms);
  201.  
  202.             /* If we have a sample size of five,
  203.              * we dequeue the last value and average the new one in
  204.              * Otherwise we just start to sum the total for staticAverage */
  205.  
  206.             if(rmsDeque.size()>5){
  207.                 maxRMS =        getMaxRMS(rmsDeque);
  208.                 total +=        rms;
  209.                 staticAverage = total/sampleCount;
  210.                 rmsDeque.removeLast();
  211.             }
  212.             else{
  213.                 total += rms;
  214.             }
  215.  
  216.             /* For User Feedback */
  217.             System.out.println(maxRMS/staticAverage);
  218.         }
  219.  
  220.         /* Determine the time at which Speech was detect (necessary for cropping later) */
  221.         this.speechDetectionTime = getSpeechDetectionTime(System.currentTimeMillis(),startTime);
  222.  
  223.         /* If we have timed-out waiting for speech we close the lines,
  224.          * return false for isActive
  225.          * otherwise true
  226.          *  */
  227.         if ((System.currentTimeMillis()-startTime) >= NOSPEECH_TIMEOUT){
  228.             mic1.stopRecording();
  229.             secondaryLine.flush();
  230.             secondaryLine.close();
  231.             this.speechDetected = false;
  232.         }
  233.         else{
  234.             this.speechDetected = true;
  235.         }
  236.  
  237.         return this.speechDetected;
  238.     }
  239.  
  240.     /** Public facing method, to be called right after isActive().
  241.      *  This method will call private helper methods to
  242.      *  Auto-stop the recording once the sound level has died down
  243.      *  Crop the recording to reduce filesize
  244.      *  Convert the recording to flac
  245.      *  
  246.      *  */
  247.     public void record(){
  248.         autoStop();
  249.         crop();
  250.         convert();     
  251.     }
  252.  
  253.     /** Crop: Will Take the previous file, discard any period of inactivity format the remainder as .WAV*/
  254.     private void crop(){
  255.         try{
  256.  
  257.             /* Load up the previous file*/
  258.             FileInputStream istream =       new FileInputStream(this.outputFile);      
  259.  
  260.             /*Calculate the number of bytes to be cropped*/
  261.             long cropLength =               WAVE_HEADER+((long)speechDetectionTime*BYTES_PER_MILLISECOND);
  262.  
  263.             /* Initialize the new cropped.wav file */
  264.             this.croppedWaveFile =          new File("cropped.wav");
  265.  
  266.             /* Create a wave input stream from the file input stream */
  267.             AudioInputStream waveStream =   new AudioInputStream(istream,audioFormat,this.outputFile.length());
  268.  
  269.             /*Discard all the bytes until where we marked activity */
  270.             waveStream.skip(cropLength);
  271.  
  272.             /*Write it to file in wave format*/
  273.             AudioSystem.write(waveStream, AudioFileFormat.Type.WAVE, this.croppedWaveFile);
  274.  
  275.             /*Close the wavefile and file stream*/
  276.             waveStream.close();
  277.             istream.close();
  278.         }
  279.         catch(IOException IOe){
  280.             IOe.printStackTrace();
  281.         }
  282.     }
  283.  
  284.     /** Using JFLACENCODER, convert files to flac. */
  285.     private void convert(){
  286.         FLAC_FileEncoder encoder1 = new FLAC_FileEncoder();
  287.         File infile =               this.croppedWaveFile;
  288.         File outfile =              new File("recording.flac");
  289.         //encoder1.useThreads(false);
  290.         encoder1.encode(infile, outfile);
  291.     }
  292.  
  293.     /** Very Similary to isSilent, except this method will record until the maxRMS falls below a given threshold*/
  294.     private void autoStop(){
  295.  
  296.         /* Similar variables as before !See isActive() Method!*/
  297.         int bytesPerSec =               audioFormat.getSampleSizeInBits() * (int) audioFormat.getSampleRate();
  298.         int sampleDataBufferSize =      bytesPerSec / this.AUTOSTOP_RATE;
  299.         int sampleCount =               0;
  300.         long startTime =                System.currentTimeMillis();
  301.         double total =                  0;
  302.         byte[] sampleDataByteArray =    new byte[sampleDataBufferSize];
  303.         LinkedList<Double> rmsDeque =   new LinkedList<Double>();
  304.         System.out.println("Recording");
  305.  
  306.         secondaryLine.start();
  307.  
  308.         /*While still speaking, keep recording*/
  309.         while (isSpeech(startTime,sampleCount++)){
  310.  
  311.             /*Blocking read assignment to the buffer*/
  312.             secondaryLine.read(sampleDataByteArray, 0, sampleDataBufferSize);
  313.  
  314.             /* Generate floating point values between 0 and 1*/
  315.             float[] fArray = Recorder.getFloatArray(sampleDataByteArray);
  316.  
  317.             /* Calculate RMS */
  318.             Double rms = Recorder.getRMS(fArray);
  319.  
  320.             /* Push to Deque and calculate an average while recieving signal and maxRMS */
  321.             rmsDeque.push(rms);
  322.             if(rmsDeque.size()>5){
  323.                 maxRMS =        getMaxRMS(rmsDeque);
  324.                 total +=        rms;
  325.                 signalAverage = total/sampleCount;
  326.                 rmsDeque.removeLast();
  327.             }
  328.             else{
  329.                 total += rms;
  330.             }
  331.  
  332.             /*For User FeedBack*/
  333.             System.out.println(maxRMS/staticAverage +"      " + maxRMS/signalAverage);
  334.         }
  335.  
  336.         System.out.println("Processing...");
  337.  
  338.         /*Stop Recording, flush lines and close*/
  339.         mic1.stopRecording();
  340.         secondaryLine.flush();
  341.         secondaryLine.close();
  342.     }
  343.  
  344.     /** Test for speech or if Timed Out. Return true if still speaking, otherwise returns false*/
  345.     private boolean isSpeech(long startTime, int count){
  346.  
  347.         /*If at least 5 samples have been taken, and we have not TIMEDOUT
  348.          * check if the MaxRMS is Below the Original Static Average
  349.          * check if the MaxRMS is A factor of 10 below the Signal Average
  350.          * if so return false, otherwise true and break the loop
  351.          * */
  352.  
  353.         if (count>5){
  354.             if ((System.currentTimeMillis()-startTime) < LONGSPEECH_TIMEOUT &&
  355.                     (this.maxRMS > this.staticAverage*(NOISE_FACTOR/2)) &&
  356.                     (maxRMS > this.signalAverage*SILENCE_FACTOR) )
  357.             {
  358.                 return true;
  359.             }
  360.             else{
  361.                 return false;
  362.             }
  363.         }
  364.         return true;       
  365.     }
  366.  
  367.     /** Take in the long time when activity was first noticed and the microphones start time
  368.      *  Determine the amount of time to be cropped from the beginning */
  369.     private int getSpeechDetectionTime(long activity,long start){
  370.         long cropTime = activity - start;
  371.  
  372.         /* In the even that the cropTime is below 200 ms
  373.          * we return 0 because we use the 200 ms for a buffer
  374.          * to ensure more efficient speech recognition
  375.          * Otherwise we return upto 200 ms before we flagged any activity
  376.          * */
  377.  
  378.         if (cropTime< SMOOTHENING_BUFFER){
  379.             return 0;
  380.         }
  381.  
  382.         return (int) (cropTime - SMOOTHENING_BUFFER);
  383.     }
  384.  
  385.     /** isSilent(), returns false if the microphone activity increases a factor of 4 above static noise level*/
  386.     private boolean isSilent(int count, long startTime){
  387.  
  388.         /* If we have sampled enough data
  389.          * and not timed out
  390.          * we check if are a factor of 4 above the background static noise
  391.          * If so we return false to break the sampling loop
  392.          * Otherwise we return true to keep looping till activity is measured or we time out
  393.          * */
  394.  
  395.         if (count>5){
  396.             if ((System.currentTimeMillis()-startTime) < NOSPEECH_TIMEOUT &&
  397.                     (this.maxRMS < this.staticAverage*this.NOISE_FACTOR))
  398.             {
  399.                 return true;
  400.             }
  401.             else{
  402.                 return false;
  403.             }
  404.         }
  405.         return true;
  406.     }
  407.  
  408.     /** Iterator through the linkedlist and determine the maximum*/
  409.     private static double getMaxRMS(LinkedList<Double> rmsDeque){
  410.  
  411.         /* Assume first is max, iterator through and replace max with maximum RMS value of the last five*/
  412.         double max = rmsDeque.getFirst();
  413.         for (double dd: rmsDeque){
  414.             if(dd>max){
  415.                 max=dd;
  416.             }
  417.         }
  418.         return max;
  419.     }
  420.  
  421.     /** Calculate the Root Mean Square and return it as double*/
  422.     private static double getRMS(float [] fArray){
  423.  
  424.         double total=0.0;
  425.  
  426.         /*Iterate through the array and square and sum all the terms*/
  427.         for (float sh : fArray){
  428.             total+=(sh*sh);
  429.         }
  430.  
  431.         /*Divide by the length of array and square root and return RMS*/
  432.         double rms = Math.pow(total/(fArray.length),0.5d);
  433.         return rms;
  434.     }
  435.  
  436.     /** Convert the byte array to a readable float format, of values between zero and one*/
  437.     private static float[] getFloatArray(byte [] audioDataByteArray){
  438.  
  439.         /* Convert from 16 bit byte array to 32 bit float
  440.          * Create buffer from the byte array as a short
  441.          * */
  442.         ShortBuffer sBuffer = ByteBuffer.wrap(audioDataByteArray).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
  443.         short[] sArray = new short[sBuffer.capacity()];
  444.         sBuffer.get(sArray);   
  445.  
  446.         /* Generate a float array with floats made from each short*/
  447.         float[] fArray = new float[sArray.length];
  448.         for (int ii = 0; ii < sArray.length; ii++) {
  449.             fArray[ii] = ((float)sArray[ii])/0x8000;
  450.         }
  451.  
  452.         return fArray;
  453.     }
  454.  
  455.     /*Tester function: main*/
  456.  
  457.  
  458.     public static void main(String[] args) throws InterruptedException, IOException {
  459.         try{
  460.             ExecutorService exs = Executors.newFixedThreadPool(20);
  461.             Recorder rr = new Recorder(exs);
  462.             if (rr.isActive()){
  463.                 rr.record();
  464.             }
  465.             else{
  466.             }
  467.             exs.shutdownNow();
  468.         }
  469.         catch(Exception ee){
  470.             ee.printStackTrace();
  471.         }
  472.     }
  473.  
  474. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement