Java extract amplitude array from recorded wave

Extract amplitude array from recorded/saved wav : From File , AudioInputStream , ByteArray of File or ByteArrayInputStream - working java source code example

 import java.io.ByteArrayInputStream;  
 import java.io.File;  
 import java.io.FileInputStream;  
 import java.io.FileOutputStream;  
 import java.io.IOException;  
 import javax.sound.sampled.AudioFileFormat;  
 import javax.sound.sampled.AudioFormat;  
 import javax.sound.sampled.AudioInputStream;  
 import javax.sound.sampled.AudioSystem;  
 import javax.sound.sampled.UnsupportedAudioFileException;  
 /**  
  * saving and extracting amplitude data from wavefile byteArray  
  *   
  * @author Ganesh Tiwari  
  */  
 public class WaveData {  
      private byte[] arrFile;  
      private byte[] audioBytes;  
      private int[] audioData;  
      private ByteArrayInputStream bis;  
      private AudioInputStream audioInputStream;  
      private AudioFormat format;  
      private double durationSec;  
      private double durationMSec;  
      public WaveData() {  
      }  
      public int[] extractAmplitudeFromFile(File wavFile) {  
           try {  
                // create file input stream  
                FileInputStream fis = new FileInputStream(wavFile);  
                // create bytearray from file  
                arrFile = new byte[(int) wavFile.length()];  
                fis.read(arrFile);  
           } catch (Exception e) {  
                System.out.println("SomeException : " + e.toString());  
           }  
           return extractAmplitudeFromFileByteArray(arrFile);  
      }  
      public int[] extractAmplitudeFromFileByteArray(byte[] arrFile) {  
           // System.out.println("File : "+wavFile+""+arrFile.length);  
           bis = new ByteArrayInputStream(arrFile);  
           return extractAmplitudeFromFileByteArrayInputStream(bis);  
      }  
      /**  
       * for extracting amplitude array the format we are using :16bit, 22khz, 1  
       * channel, littleEndian,  
       *   
       * @return PCM audioData  
       * @throws Exception  
       */  
      public int[] extractAmplitudeFromFileByteArrayInputStream(ByteArrayInputStream bis) {  
           try {  
                audioInputStream = AudioSystem.getAudioInputStream(bis);  
           } catch (UnsupportedAudioFileException e) {  
                System.out.println("unsupported file type, during extract amplitude");  
                e.printStackTrace();  
           } catch (IOException e) {  
                System.out.println("IOException during extracting amplitude");  
                e.printStackTrace();  
           }  
           // float milliseconds = (long) ((audioInputStream.getFrameLength() *  
           // 1000) / audioInputStream.getFormat().getFrameRate());  
           // durationSec = milliseconds / 1000.0;  
           return extractAmplitudeDataFromAudioInputStream(audioInputStream);  
      }  
      public int[] extractAmplitudeDataFromAudioInputStream(AudioInputStream audioInputStream) {  
           format = audioInputStream.getFormat();  
           audioBytes = new byte[(int) (audioInputStream.getFrameLength() * format.getFrameSize())];  
           // calculate durations  
           durationMSec = (long) ((audioInputStream.getFrameLength() * 1000) / audioInputStream.getFormat().getFrameRate());  
           durationSec = durationMSec / 1000.0;  
           // System.out.println("The current signal has duration "+durationSec+" Sec");  
           try {  
                audioInputStream.read(audioBytes);  
           } catch (IOException e) {  
                System.out.println("IOException during reading audioBytes");  
                e.printStackTrace();  
           }  
           return extractAmplitudeDataFromAmplitudeByteArray(format, audioBytes);  
      }  
      public int[] extractAmplitudeDataFromAmplitudeByteArray(AudioFormat format, byte[] audioBytes) {  
           // convert  
           // TODO: calculate duration here  
           audioData = null;  
           if (format.getSampleSizeInBits() == 16) {  
                int nlengthInSamples = audioBytes.length / 2;  
                audioData = new int[nlengthInSamples];  
                if (format.isBigEndian()) {  
                     for (int i = 0; i < nlengthInSamples; i++) {  
                          /* First byte is MSB (high order) */  
                          int MSB = audioBytes[2 * i];  
                          /* Second byte is LSB (low order) */  
                          int LSB = audioBytes[2 * i + 1];  
                          audioData[i] = MSB << 8 | (255 & LSB);  
                     }  
                } else {  
                     for (int i = 0; i < nlengthInSamples; i++) {  
                          /* First byte is LSB (low order) */  
                          int LSB = audioBytes[2 * i];  
                          /* Second byte is MSB (high order) */  
                          int MSB = audioBytes[2 * i + 1];  
                          audioData[i] = MSB << 8 | (255 & LSB);  
                     }  
                }  
           } else if (format.getSampleSizeInBits() == 8) {  
                int nlengthInSamples = audioBytes.length;  
                audioData = new int[nlengthInSamples];  
                if (format.getEncoding().toString().startsWith("PCM_SIGN")) {  
                     // PCM_SIGNED  
                     for (int i = 0; i < audioBytes.length; i++) {  
                          audioData[i] = audioBytes[i];  
                     }  
                } else {  
                     // PCM_UNSIGNED  
                     for (int i = 0; i < audioBytes.length; i++) {  
                          audioData[i] = audioBytes[i] - 128;  
                     }  
                }  
           }// end of if..else  
                // System.out.println("PCM Returned===============" +  
                // audioData.length);  
           return audioData;  
      }  
      public byte[] getAudioBytes() {  
           return audioBytes;  
      }  
      public double getDurationSec() {  
           return durationSec;  
      }  
      public double getDurationMiliSec() {  
           return durationMSec;  
      }  
      public int[] getAudioData() {  
           return audioData;  
      }  
      public AudioFormat getFormat() {  
           return format;  
      }  
 }  

##

4 comments :

  1. Nor working dude.
    Im working with a PCM, 8000hz, 16bit, littleEndian, 1 channel audio stream (AudioInputStream) and the int[] audioData[] that function is returning me is the array fill only with zero.

    What am i doing wrong? or do i have to change something? work with a restriction??

    Im working on a Java vowel recognizer through sound sampling. I need the audioData to perform on it the FFT and get the frequency spectrum.

    Need some help to get first the audioData.

    Thnks.

    ReplyDelete
    Replies
    1. Could you please add some break points (or sysout) in code and debug the code? This code should work for all settings.

      As far as i remember I had run this code with 16KHz, 16 bit. Could you please try using this setting instead.

      Delete
  2. Hey guys, I just saw this code and I was wondering, anyone knows how to write a java program that allows us to open a wav file, change the amplitude and save it as a new wav file? I would appreciate any help.

    ReplyDelete
  3. Think I found a bug for 8 bit unsigned samples in the code above.

    Java regards a byte-variable as a signed variable, so we can't just subtract 128 for all sample-values. For "negative" values we must instead add 128, I think.

    E.g. the sampled unsigned value 10000000 (128 unsigned) should mean that we are in the middle of the value-range. It should actually mean 0, but java sees it as -128, and if we subtract 128 we'll get -256, which isn't what we want at all.

    And the "highest" sample-value possible with 8 bits, 11111111, means -1 to java if it's in a byte-variable. We'd get the value -129 here with the old method, but we would expect 127.

    For all "positive" values 00000000 - 01111111 it works fine to subtract 128 as before, so something like this would work better:

    // PCM_UNSIGNED
    for (int i = 0; i < audioBytes.length; i++)
    {
    if (audioBytes[i] >= 0)
    _audioData[i] = audioBytes[i] - 128;
    else
    _audioData[i] = audioBytes[i] + 128;
    }

    (Or e.g. you could "shift" the byte-value into an int-variable before subtracting 128.)

    ReplyDelete

Your Comment and Question will help to make this blog better...