转载自:http://ganeshtiwaridotcomdotnp.blogspot.com/2011/12/java-extract-amplitude-array-from.html


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;
      }
 }  

留言:

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.)

12-29 20:26